From eccc37156ac618f5711157b5430dee8d16188a9b Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Tue, 2 Jul 2024 19:19:05 +0200 Subject: [PATCH] OpenVidu v3 --- ci-scripts/commons/build.sh | 248 - ci-scripts/commons/bump.sh | 307 - ci-scripts/commons/test-utils.sh | 161 - ci-scripts/kurento-snapshots.xml | 43 - ci-scripts/openvidu-e2e-tests.sh | 241 - openvidu-browser/.gitignore | 62 - openvidu-browser/.npmignore | 1 - openvidu-browser/.prettierrc | 10 - openvidu-browser/LICENSE | 201 - openvidu-browser/config/replace_for_ts44.sh | 7 - openvidu-browser/config/tsconfig.json | 20 - openvidu-browser/config/tslint.json | 154 - openvidu-browser/generate-docs.sh | 19 - openvidu-browser/package-lock.json | 2511 -- openvidu-browser/package.json | 48 - openvidu-browser/src/Main.ts | 9 - openvidu-browser/src/OpenVidu/Connection.ts | 217 - .../src/OpenVidu/EventDispatcher.ts | 112 - openvidu-browser/src/OpenVidu/Filter.ts | 285 - .../src/OpenVidu/LocalRecorder.ts | 416 - openvidu-browser/src/OpenVidu/OpenVidu.ts | 1301 - openvidu-browser/src/OpenVidu/Publisher.ts | 875 - openvidu-browser/src/OpenVidu/Session.ts | 1804 -- openvidu-browser/src/OpenVidu/Stream.ts | 1877 -- .../src/OpenVidu/StreamManager.ts | 621 - openvidu-browser/src/OpenVidu/Subscriber.ts | 101 - openvidu-browser/src/OpenVidu/tsconfig.json | 35 - .../Enums/LocalRecorderState.ts | 23 - .../OpenViduInternal/Enums/OpenViduError.ts | 141 - .../src/OpenViduInternal/Enums/TypeOfVideo.ts | 23 - .../OpenViduInternal/Enums/VideoInsertMode.ts | 42 - .../Events/ConnectionEvent.ts | 61 - .../Events/ConnectionPropertyChangedEvent.ts | 68 - .../src/OpenViduInternal/Events/Event.ts | 83 - .../Events/EventMap/EventMap.ts | 21 - .../Events/EventMap/PublisherEventMap.ts | 80 - .../Events/EventMap/SessionEventMap.ts | 225 - .../Events/EventMap/StreamManagerEventMap.ts | 90 - .../OpenViduInternal/Events/ExceptionEvent.ts | 136 - .../OpenViduInternal/Events/FilterEvent.ts | 43 - .../Events/NetworkQualityLevelChangedEvent.ts | 56 - .../Events/PublisherSpeakingEvent.ts | 54 - .../OpenViduInternal/Events/RecordingEvent.ts | 71 - .../Events/SessionDisconnectedEvent.ts | 80 - .../OpenViduInternal/Events/SignalEvent.ts | 64 - .../Events/SpeechToTextEvent.ts | 72 - .../OpenViduInternal/Events/StreamEvent.ts | 113 - .../Events/StreamManagerEvent.ts | 49 - .../Events/StreamPropertyChangedEvent.ts | 83 - .../OpenViduInternal/Events/Types/Types.ts | 42 - .../Events/VideoElementEvent.ts | 45 - .../Private/CustomMediaStreamConstraints.ts | 22 - .../Interfaces/Private/IceServerProperties.ts | 21 - .../Private/InboundStreamOptions.ts | 34 - .../Private/LocalConnectionOptions.ts | 42 - .../Private/OutboundStreamOptions.ts | 23 - .../Private/RemoteConnectionOptions.ts | 25 - .../Interfaces/Private/SessionOptions.ts | 22 - .../Interfaces/Private/SignalOptions.ts | 24 - .../Interfaces/Private/StreamOptionsServer.ts | 32 - .../Interfaces/Public/Capabilities.ts | 41 - .../Interfaces/Public/Device.ts | 36 - .../Public/OpenViduAdvancedConfiguration.ts | 79 - .../Interfaces/Public/PublisherProperties.ts | 101 - .../Interfaces/Public/SignalOptions.ts | 40 - .../Interfaces/Public/StreamManagerVideo.ts | 57 - .../Interfaces/Public/SubscriberProperties.ts | 41 - .../KurentoUtils/kurento-jsonrpc/Mapper.js | 52 - .../kurento-jsonrpc/clients/index.js | 20 - .../kurento-jsonrpc/clients/jsonrpcclient.js | 280 - .../clients/transports/index.js | 20 - .../transports/webSocketWithReconnection.js | 161 - .../KurentoUtils/kurento-jsonrpc/index.js | 687 - .../kurento-jsonrpc/packers/JsonRPC.js | 85 - .../kurento-jsonrpc/packers/XmlRPC.js | 10 - .../kurento-jsonrpc/packers/index.js | 5 - .../OpenViduInternal/Logger/ConsoleLogger.ts | 41 - .../OpenViduInternal/Logger/OpenViduLogger.ts | 285 - .../Logger/OpenViduLoggerConfiguration.ts | 5 - .../ScreenSharing/Screen-Capturing-Auto.js | 233 - .../ScreenSharing/Screen-Capturing.js | 162 - .../src/OpenViduInternal/Utils/Platform.ts | 233 - .../OpenViduInternal/WebRtcPeer/WebRtcPeer.ts | 600 - .../WebRtcStats/WebRtcStats.ts | 471 - openvidu-browser/src/index.ts | 51 - openvidu-browser/tsconfig.json | 36 - openvidu-client/.gitignore | 1 - openvidu-client/README.md | 14 - openvidu-client/pom.xml | 106 - .../io/openvidu/client/OpenViduClient.java | 217 - .../io/openvidu/client/OpenViduException.java | 88 - .../openvidu/client/ServerJsonRpcHandler.java | 222 - .../client/internal/IceCandidate.java | 33 - .../client/internal/IceCandidateInfo.java | 70 - .../client/internal/JsonRoomUtils.java | 107 - .../client/internal/MediaErrorInfo.java | 55 - .../client/internal/Notification.java | 90 - .../internal/ParticipantEvictedInfo.java | 30 - .../internal/ParticipantJoinedInfo.java | 54 - .../client/internal/ParticipantLeftInfo.java | 54 - .../internal/ParticipantPublishedInfo.java | 69 - .../internal/ParticipantUnpublishedInfo.java | 54 - .../client/internal/ProtocolElements.java | 252 - .../client/internal/RoomClosedInfo.java | 54 - .../client/internal/SendMessageInfo.java | 80 - .../client/test/OpenViduClientTest.java | 74 - openvidu-components-angular/.prettierignore | 5 + openvidu-components-angular/README.md | 8 +- openvidu-components-angular/angular.json | 63 +- openvidu-components-angular/docs.md | 5 - .../e2e/angular.test.ts | 12 +- openvidu-components-angular/e2e/config.ts | 4 +- .../e2e/selenium.conf.ts | 14 +- .../e2e/utils.po.test.ts | 83 +- .../e2e/webcomponent-app/app.js | 340 +- .../e2e/webcomponent-app/index.html | 8 +- .../webcomponent-app/utils/media-devices.js | 29 +- .../webcomponent-e2e/api-directives.test.ts | 673 + .../e2e/webcomponent-e2e/captions.test.ts | 181 + .../e2e/webcomponent-e2e/chat.test.ts | 117 + .../e2e/webcomponent-e2e/events.test.ts | 644 + .../webcomponent-e2e/media-devices.test.ts | 219 + .../e2e/webcomponent-e2e/panels.test.ts | 225 + .../webcomponent-e2e/screensharing.test.ts | 326 + .../e2e/webcomponent-e2e/stream.test.ts | 863 + .../e2e/webcomponent-e2e/toolbar.test.ts | 63 + .../e2e/webcomponent.pro.test.ts | 28 +- .../e2e/webcomponent.test.ts | 2328 +- openvidu-components-angular/generate-docs.sh | 4 +- .../migration-guide.md | 280 + .../openvidu-webcomponent-build.js | 13 +- openvidu-components-angular/package-lock.json | 6618 +++-- openvidu-components-angular/package.json | 96 +- .../projects/openvidu-angular/README.md | 85 - .../openvidu-angular/doc/.compodocrc.json | 20 - .../openvidu-angular/package-lock.json | 403 - .../projects/openvidu-angular/package.json | 18 - .../admin/dashboard/dashboard.component.html | 138 - .../admin/dashboard/dashboard.component.ts | 164 - .../src/lib/admin/login/login.component.css | 75 - .../src/lib/admin/login/login.component.html | 44 - .../src/lib/admin/login/login.component.ts | 110 - .../audio-wave/audio-wave.component.html | 5 - .../audio-wave/audio-wave.component.ts | 41 - .../dialogs/recording-dialog.component.ts | 36 - .../components/layout/layout.component.html | 18 - .../lib/components/layout/layout.component.ts | 135 - .../activities-panel.component.css | 86 - .../activities-panel.component.ts | 145 - .../recording-activity.component.css | 149 - .../recording-activity.component.ts | 232 - .../background-effects-panel.component.ts | 66 - .../components/panel/panel.component.spec.ts | 25 - .../participant-panel-item.component.css | 60 - .../participant-panel-item.component.ts | 121 - .../participants-panel.component.ts | 119 - .../settings-panel.component.css | 54 - .../pre-join/pre-join.component.css | 165 - .../pre-join/pre-join.component.html | 72 - .../components/pre-join/pre-join.component.ts | 135 - .../components/session/session.component.ts | 466 - .../audio-devices/audio-devices.component.css | 17 - .../audio-devices.component.html | 34 - .../audio-devices/audio-devices.component.ts | 83 - .../settings/captions/captions.component.css | 27 - .../lang-selector/lang-selector.component.css | 7 - .../lang-selector.component.html | 17 - .../nickname-input.component.css | 21 - .../nickname-input.component.html | 16 - .../nickname-input.component.spec.ts | 25 - .../nickname-input.component.ts | 40 - .../video-devices/video-devices.component.css | 25 - .../video-devices.component.html | 41 - .../video-devices/video-devices.component.ts | 119 - .../components/stream/stream.component.css | 132 - .../components/stream/stream.component.html | 95 - .../lib/components/stream/stream.component.ts | 289 - .../components/toolbar/toolbar.component.css | 187 - .../lib/components/video/video.component.ts | 57 - .../videoconference.component.css | 31 - .../videoconference.component.ts | 806 - .../src/lib/config/custom-cdk-overlay.ts | 30 - .../config/custom-flexlayout-breakpoints.ts | 29 - .../src/lib/config/openvidu-angular.config.ts | 8 - .../api/broadcasting-activity.directive.ts | 55 - .../api/recording-activity.directive.ts | 103 - .../lib/directives/api/stream.directive.ts | 255 - .../template/openvidu-angular.directive.ts | 781 - .../src/lib/matchers/nickname.matcher.ts | 13 - .../src/lib/models/broadcasting.model.ts | 13 - .../src/lib/models/icon.model.ts | 7 - .../src/lib/models/panel.model.ts | 22 - .../src/lib/models/participant.model.ts | 391 - .../src/lib/models/recording.model.ts | 19 - .../src/lib/models/signal.model.ts | 8 - .../src/lib/models/token.model.ts | 13 - .../src/lib/models/video-type.model.ts | 13 - .../src/lib/pipes/participant.pipe.ts | 37 - .../broadcasting/broadcasting.service.ts | 57 - .../src/lib/services/chat/chat.service.ts | 82 - .../openvidu-angular.config.service.spec.ts | 22 - .../config/openvidu-angular.config.service.ts | 171 - .../lib/services/openvidu/openvidu.service.ts | 663 - .../participant/participant.service.ts | 531 - .../services/recording/recording.service.ts | 130 - .../openvidu-angular/tsconfig.lib.json | 24 - .../.browserslistrc | 0 .../.prettierrc | 0 .../openvidu-components-angular/README.md | 95 + .../doc/.compodocrc.json | 30 + .../doc/favicon.ico | Bin .../doc/scripts/generate-directive-tables.js | 170 + .../scripts/generate-directive-tutorials.js | 75 + .../doc/styles}/style.css | 53 +- .../doc/tsconfig.doc.json | 3 +- .../karma.conf.js | 0 .../ng-package.json | 2 +- .../openvidu-components-angular/package.json | 18 + .../admin-dashboard.component.html | 162 + .../admin-dashboard.component.md | 8 + .../admin-dashboard.component.scss} | 111 +- .../admin-dashboard.component.spec.ts} | 10 +- .../admin-dashboard.component.ts | 259 + .../admin-login/admin-login.component.html | 37 + .../admin-login/admin-login.component.md | 9 + .../admin-login/admin-login.component.scss | 37 + .../admin-login.component.spec.ts} | 0 .../admin-login/admin-login.component.ts | 87 + .../audio-wave/audio-wave.component.scss} | 2 +- .../audio-wave/audio-wave.component.spec.ts | 0 .../audio-wave/audio-wave.component.ts | 15 + .../avatar-profile.component.scss} | 3 +- .../avatar-profile.component.spec.ts | 0 .../avatar-profile.component.ts | 6 +- .../captions/captions.component.html | 0 .../captions/captions.component.scss} | 29 +- .../captions/captions.component.spec.ts | 0 .../components/captions/captions.component.ts | 115 +- .../dialogs/delete-recording.component.ts | 0 .../components/dialogs/dialog.component.ts | 2 +- .../dialogs/pro-feature-dialog.component.ts | 0 .../dialogs/recording-dialog.component.ts | 46 + .../components/layout/layout.component.html | 37 + .../lib/components/layout/layout.component.md | 26 + .../components/layout/layout.component.scss} | 44 +- .../layout/layout.component.spec.ts | 0 .../lib/components/layout/layout.component.ts | 218 + .../media-element.component.scss} | 0 .../media-element.component.spec.ts} | 2 +- .../media-element/media-element.component.ts | 114 + .../activities-panel.component.html | 13 +- .../activities-panel.component.md | 9 + .../activities-panel.component.scss | 77 + .../activities-panel.component.spec.ts | 0 .../activities-panel.component.ts | 138 + .../broadcasting-activity.component.html | 34 +- .../broadcasting-activity.component.md | 7 + .../broadcasting-activity.component.scss} | 3 + .../broadcasting-activity.component.spec.ts | 0 .../broadcasting-activity.component.ts | 107 +- .../recording-activity.component.html | 50 +- .../recording-activity.component.md | 7 + .../recording-activity.component.scss | 167 + .../recording-activity.component.spec.ts | 0 .../recording-activity.component.ts | 241 + .../background-effects-panel.component.html | 28 +- .../background-effects-panel.component.scss} | 3 +- ...background-effects-panel.component.spec.ts | 0 .../background-effects-panel.component.ts | 65 + .../chat-panel/chat-panel.component.html | 19 +- .../panel/chat-panel/chat-panel.component.md | 19 + .../chat-panel/chat-panel.component.scss} | 40 +- .../chat-panel/chat-panel.component.spec.ts | 0 .../panel/chat-panel/chat-panel.component.ts | 67 +- .../lib/components/panel/panel.component.html | 0 .../lib/components/panel/panel.component.md | 30 + .../components/panel/panel.component.scss} | 0 .../lib/components/panel/panel.component.ts | 173 +- .../participant-panel-item.component.html | 11 +- .../participant-panel-item.component.md | 27 + .../participant-panel-item.component.scss | 59 + .../participant-panel-item.component.spec.ts | 0 .../participant-panel-item.component.ts | 96 + .../participants-panel.component.html | 0 .../participants-panel.component.md | 26 + .../participants-panel.component.scss} | 0 .../participants-panel.component.spec.ts | 0 .../participants-panel.component.ts | 117 + .../settings-panel.component.html | 25 +- .../settings-panel.component.scss | 53 + .../settings-panel.component.spec.ts | 0 .../settings-panel.component.ts | 25 +- .../pre-join/pre-join.component.html | 65 + .../pre-join/pre-join.component.scss | 199 + .../pre-join/pre-join.component.spec.ts | 0 .../components/pre-join/pre-join.component.ts | 167 + .../components/session/session.component.html | 8 +- .../session/session.component.scss} | 25 +- .../session/session.component.spec.ts | 0 .../components/session/session.component.ts | 484 + .../audio-devices.component.html | 55 + .../audio-devices.component.scss | 84 + .../audio-devices.component.spec.ts | 0 .../audio-devices/audio-devices.component.ts | 92 + .../settings/captions/captions.component.html | 2 +- .../settings/captions/captions.component.scss | 33 + .../captions/captions.component.spec.ts | 0 .../settings/captions/captions.component.ts | 26 +- .../lang-selector.component.html | 18 + .../lang-selector.component.scss | 12 + .../lang-selector.component.spec.ts | 0 .../lang-selector/lang-selector.component.ts | 33 +- .../participant-name-input.component.html | 19 + .../participant-name-input.component.scss | 62 + .../participant-name-input.component.spec.ts | 25 + .../participant-name-input.component.ts | 77 + .../video-devices.component.html | 40 + .../video-devices.component.scss | 83 + .../video-devices.component.spec.ts | 0 .../video-devices/video-devices.component.ts | 120 + .../components/stream/stream.component.html | 80 + .../lib/components/stream/stream.component.md | 23 + .../components/stream/stream.component.scss | 105 + .../stream/stream.component.spec.ts | 0 .../lib/components/stream/stream.component.ts | 205 + .../components/toolbar/toolbar.component.html | 160 +- .../components/toolbar/toolbar.component.md | 40 + .../components/toolbar/toolbar.component.scss | 246 + .../toolbar/toolbar.component.spec.ts | 0 .../components/toolbar/toolbar.component.ts | 457 +- .../videoconference.component.html | 77 +- .../videoconference.component.md | 54 + .../videoconference.component.scss | 35 + .../videoconference.component.spec.ts | 0 .../videoconference.component.ts | 547 + .../src/lib/config/custom-cdk-overlay.ts | 31 + .../openvidu-components-angular.config.ts | 8 + .../api/activities-panel.directive.ts | 14 +- .../src/lib/directives/api/admin.directive.ts | 22 +- .../directives/api/api.directive.module.ts | 83 +- .../lib/directives/api/internals.directive.ts | 1 - .../api/participant-panel-item.directive.ts | 8 +- .../lib/directives/api/stream.directive.ts | 162 + .../lib/directives/api/toolbar.directive.ts | 231 +- .../api/videoconference.directive.ts | 443 +- ...du-components-angular.directive.module.ts} | 8 +- .../openvidu-components-angular.directive.ts | 1803 ++ .../src/lib/lang/cn.json | 61 +- .../src/lib/lang/de.json | 61 +- .../src/lib/lang/en.json | 62 +- .../src/lib/lang/es.json | 61 +- .../src/lib/lang/fr.json | 63 +- .../src/lib/lang/hi.json | 61 +- .../src/lib/lang/it.json | 61 +- .../src/lib/lang/ja.json | 63 +- .../src/lib/lang/nl.json | 61 +- .../src/lib/lang/pt.json | 64 +- .../src/lib/models/background-effect.model.ts | 0 .../src/lib/models/broadcasting.model.ts | 33 + .../src/lib/models/caption.model.ts | 13 +- .../src/lib/models/chat.model.ts | 2 +- .../src/lib/models/data-topic.model.ts | 19 + .../src/lib/models/device.model.ts | 0 .../src/lib/models/dialog.model.ts | 0 .../src/lib/models/lang.model.ts | 0 .../src/lib/models/layout.model.ts | 152 +- .../src/lib/models/linkifier.model.ts | 0 .../src/lib/models/logger.model.ts | 0 .../lib/models/notification-options.model.ts | 3 +- .../src/lib/models/openvidu.model.ts | 0 .../src/lib/models/panel.model.ts | 59 + .../src/lib/models/participant.model.ts | 525 + .../src/lib/models/recording.model.ts | 60 + .../src/lib/models/room.model.ts | 11 + .../src/lib/models/storage.model.ts | 6 +- ...idu-components-angular.material.module.ts} | 14 +- .../openvidu-components-angular.module.ts} | 138 +- .../src/lib/pipes/linkify.pipe.ts | 0 .../src/lib/pipes/participant.pipe.ts | 44 + .../src/lib/pipes/recording.pipe.ts | 0 .../src/lib/pipes/translate.pipe.ts | 0 .../services/action/action.service.mock.ts | 0 .../services/action/action.service.spec.ts | 0 .../src/lib/services/action/action.service.ts | 74 +- .../broadcasting/broadcasting.service.spec.ts | 0 .../broadcasting/broadcasting.service.ts | 102 + .../services/caption/caption.service.spec.ts | 0 .../lib/services/caption/caption.service.ts | 7 +- .../cdk-overlay/cdk-overlay.service.mock.ts | 0 .../cdk-overlay/cdk-overlay.service.spec.ts | 0 .../cdk-overlay/cdk-overlay.service.ts | 4 +- .../lib/services/chat/chat.service.mock.ts | 0 .../lib/services/chat/chat.service.spec.ts | 0 .../src/lib/services/chat/chat.service.ts | 85 + ...components-angular.config.service.mock.ts} | 9 +- ...-components-angular.config.service.spec.ts | 22 + ...nvidu-components-angular.config.service.ts | 374 + .../services/device/device.service.mock.ts | 0 .../services/device/device.service.spec.ts | 16 +- .../src/lib/services/device/device.service.ts | 111 +- .../document/document.service.mock.ts | 0 .../document/document.service.spec.ts | 0 .../lib/services/document/document.service.ts | 8 +- .../services/layout/layout.service.mock.ts | 0 .../services/layout/layout.service.spec.ts | 0 .../src/lib/services/layout/layout.service.ts | 2 +- .../services/logger/logger.service.mock.ts | 0 .../services/logger/logger.service.spec.ts | 0 .../src/lib/services/logger/logger.service.ts | 4 +- .../openvidu/openvidu.service.mock.ts | 2 +- .../openvidu/openvidu.service.spec.ts | 0 .../lib/services/openvidu/openvidu.service.ts | 448 + .../lib/services/panel/panel.service.spec.ts | 0 .../src/lib/services/panel/panel.service.ts | 57 +- .../participant/participant.service.mock.ts | 10 +- .../participant/participant.service.spec.ts | 0 .../participant/participant.service.ts | 547 + .../platform/platform.service.mock.ts | 0 .../platform/platform.service.spec.ts | 0 .../lib/services/platform/platform.service.ts | 0 .../recording/recording.service.spec.ts | 0 .../services/recording/recording.service.ts | 233 + .../services/storage/storage.service.mock.ts | 0 .../services/storage/storage.service.spec.ts | 0 .../lib/services/storage/storage.service.ts | 57 +- .../translate/translate.service.spec.ts | 0 .../services/translate/translate.service.ts | 32 +- .../virtual-background.service.spec.ts | 0 .../virtual-background.service.ts | 121 +- .../src/public-api.ts | 23 +- .../src/test.ts | 0 .../tsconfig.lib.json | 20 + .../tsconfig.lib.prod.json | 0 .../tsconfig.spec.json | 0 .../admin-dashboard.component.html | 9 +- .../admin-dashboard.component.ts | 58 +- .../src/app/app.module.ts | 8 +- .../app/dashboard/dashboard.component.html | 36 +- .../app/dashboard/dashboard.component.scss | 5 + .../src/app/dashboard/dashboard.component.ts | 11 +- .../src/app/openvidu-call/call.component.html | 67 +- .../src/app/openvidu-call/call.component.ts | 233 +- .../openvidu-webcomponent.component.html | 68 +- .../openvidu-webcomponent.component.scss | 32 +- .../openvidu-webcomponent.component.ts | 575 +- .../openvidu-webcomponent.module.ts | 8 +- .../tsconfig.openvidu-webcomponent.json | 29 +- .../src/app/services/rest.service.ts | 221 +- .../app/testing-app/testing.component.html | 16 +- .../src/app/testing-app/testing.component.ts | 155 +- .../src/environments/environment.testing.ts | 4 +- openvidu-components-angular/src/polyfills.ts | 63 - .../src/proxy.conf.json | 6 + .../src/proxy.config.json | 6 - openvidu-components-angular/src/styles.scss | 87 +- openvidu-components-angular/tsconfig.app.json | 4 +- .../tsconfig.base.json | 5 +- openvidu-components-angular/tsconfig.json | 3 +- openvidu-components-angular/tslint.json | 152 - openvidu-java-client/README.md | 17 - openvidu-java-client/generate-docs.sh | 20 - openvidu-java-client/pom.xml | 237 - .../io/openvidu/java/client/Connection.java | 484 - .../java/client/ConnectionProperties.java | 698 - .../openvidu/java/client/ConnectionType.java | 20 - .../java/client/IceServerProperties.java | 392 - .../openvidu/java/client/KurentoOptions.java | 224 - .../io/openvidu/java/client/MediaMode.java | 35 - .../io/openvidu/java/client/OpenVidu.java | 845 - .../java/client/OpenViduException.java | 35 - .../java/client/OpenViduHttpException.java | 42 - .../client/OpenViduJavaClientException.java | 35 - .../io/openvidu/java/client/OpenViduRole.java | 43 - .../io/openvidu/java/client/Publisher.java | 179 - .../io/openvidu/java/client/Recording.java | 271 - .../openvidu/java/client/RecordingLayout.java | 53 - .../openvidu/java/client/RecordingMode.java | 41 - .../java/client/RecordingProperties.java | 724 - .../java/io/openvidu/java/client/Session.java | 800 - .../java/client/SessionProperties.java | 454 - .../io/openvidu/java/client/TokenOptions.java | 142 - .../java/io/openvidu/java/client/Utils.java | 36 - .../io/openvidu/java/client/VideoCodec.java | 26 - .../client/OpenViduHttpExceptionTest.java | 15 - .../OpenViduJavaClientExceptionTest.java | 15 - .../client/test/ConnectionPropertiesTest.java | 107 - .../client/test/OpenViduConstructorTest.java | 96 - .../client/test/RecordingPropertiesTest.java | 155 - .../client/test/SessionPropertiesTest.java | 50 - .../client/test/UtilsFormatCheckerTest.java | 58 - openvidu-node-client/.gitignore | 50 - openvidu-node-client/.npmignore | 1 - openvidu-node-client/.prettierrc | 10 - openvidu-node-client/README.md | 16 - openvidu-node-client/config/tsconfig.json | 20 - openvidu-node-client/config/tslint.json | 154 - openvidu-node-client/config/typedoc.js | 22 - openvidu-node-client/generate-docs.sh | 19 - openvidu-node-client/package-lock.json | 900 - openvidu-node-client/package.json | 28 - openvidu-node-client/src/Connection.ts | 278 - .../src/ConnectionProperties.ts | 160 - openvidu-node-client/src/ConnectionType.ts | 33 - .../src/IceServerProperties.ts | 48 - .../src/Logger/ConsoleLogger.ts | 41 - .../src/Logger/OpenViduLogger.ts | 62 - openvidu-node-client/src/MediaMode.ts | 31 - openvidu-node-client/src/OpenVidu.ts | 782 - openvidu-node-client/src/OpenViduRole.ts | 37 - openvidu-node-client/src/Publisher.ts | 110 - openvidu-node-client/src/Recording.ts | 193 - openvidu-node-client/src/RecordingLayout.ts | 46 - openvidu-node-client/src/RecordingMode.ts | 33 - .../src/RecordingProperties.ts | 125 - openvidu-node-client/src/Session.ts | 730 - openvidu-node-client/src/SessionProperties.ts | 96 - openvidu-node-client/src/TokenOptions.ts | 67 - openvidu-node-client/src/VideoCodec.ts | 10 - openvidu-node-client/src/index.ts | 16 - openvidu-node-client/tsconfig.json | 27 - openvidu-server/.gitignore | 4 - openvidu-server/LICENSE | 202 - openvidu-server/NOTICE | 13 - openvidu-server/README.md | 18 - .../ce/aws/CF-OpenVidu.yaml.template | 581 - .../ce/aws/cfn-mkt-ov-ce-ami.yaml.template | 199 - .../deployments/ce/aws/createAMI.sh | 128 - .../deployments/ce/docker-compose/.env | 200 - .../docker-compose.override.yml | 31 - .../ce/docker-compose/docker-compose.yml | 120 - .../ce/docker-compose/install_openvidu.sh | 328 - .../deployments/ce/docker-compose/openvidu | 305 - .../docker-compose/base-services/.env | 52 - .../base-services/base-services | 377 - .../base-services/docker-compose.override.yml | 32 - .../base-services/docker-compose.yml | 134 - .../install_ov_enterprise_ha_base.sh | 324 - .../enterprise-ha/docker-compose/node/.env | 327 - .../docker-compose/node/beats/filebeat.yml | 88 - .../node/beats/metricbeat-elasticsearch.yml | 44 - .../docker-compose/node/docker-compose.yml | 155 - .../install_openvidu_enterprise_ha_node.sh | 365 - .../docker-compose/node/openvidu | 337 - .../aws/CF-OpenVidu-Enterprise.yaml.template | 1432 - .../cfn-crete-ov-aws-asg-ami.yaml.template | 299 - .../deployments/enterprise/aws/createAMI.sh | 137 - .../CF-OpenVidu-Enterprise-dev-master.yaml | 1377 - .../deployments/enterprise/aws/dev/README.md | 20 - .../cfn-crete-ov-aws-asg-ami.yaml.template | 296 - .../enterprise/aws/dev/createAMI.sh | 111 - .../enterprise/aws/dev/deploy_cf.sh | 169 - .../aws/dev/test_sqs_unique_messages.sh | 18 - .../enterprise/docker-compose/.env | 436 - .../docker-compose/beats/filebeat.yml | 51 - .../docker-compose/beats/metricbeat.yml | 40 - .../cluster/aws/openvidu_autodiscover.sh | 18 - .../cluster/aws/openvidu_drop.sh | 11 - .../cluster/aws/openvidu_launch_kms.sh | 65 - .../docker-compose/docker-compose.yml | 189 - ...install_openvidu_enterprise_master_node.sh | 188 - .../enterprise/docker-compose/openvidu | 363 - .../external-turn/aws/CF-External-Turn.yml | 375 - .../external-turn/docker-compose/.env | 12 - .../external-turn/docker-compose/certbot.sh | 37 - .../docker-compose/docker-compose.yml | 57 - .../install_openvidu_external_coturn.sh | 111 - .../pro/aws/CF-OpenVidu-Pro.yaml.template | 1158 - .../pro/aws/cfn-mkt-kms-ami.yaml.template | 221 - .../pro/aws/cfn-mkt-ov-ami.yaml.template | 178 - .../deployments/pro/aws/createAMI.sh | 212 - .../deployments/pro/aws/delete_amis.sh | 24 - .../pro/aws/get_ov_media_node_ami_id.sh | 39 - .../media-node/beats/copy_config_files.sh | 6 - .../media-node/beats/filebeat.yml | 64 - .../beats/metricbeat-elasticsearch.yml | 38 - .../media-node/docker-compose.yml | 43 - .../media-node/install_media_node.sh | 394 - .../pro/docker-compose/media-node/media_node | 314 - .../pro/docker-compose/mono-node/.env | 349 - .../mono-node/beats/filebeat.yml | 88 - .../beats/metricbeat-elasticsearch.yml | 44 - .../mono-node/docker-compose.override.yml | 31 - .../mono-node/docker-compose.yml | 178 - .../install_openvidu_pro_mono_node.sh | 416 - .../pro/docker-compose/mono-node/openvidu | 422 - .../docker-compose/openvidu-server-pro/.env | 420 - .../openvidu-server-pro/beats/filebeat.yml | 51 - .../openvidu-server-pro/beats/metricbeat.yml | 40 - .../cluster/aws/openvidu_autodiscover.sh | 18 - .../cluster/aws/openvidu_drop.sh | 11 - .../cluster/aws/openvidu_launch_kms.sh | 65 - .../docker-compose.override.yml | 31 - .../openvidu-server-pro/docker-compose.yml | 222 - .../install_openvidu_pro.sh | 487 - .../openvidu-server-pro/openvidu | 389 - openvidu-server/docker/.gitignore | 5 - .../docker/openvidu-coturn/Dockerfile | 20 - .../docker/openvidu-coturn/README.md | 4 - .../docker/openvidu-coturn/create_image.sh | 8 - .../openvidu-coturn/detect-external-ip.sh | 122 - .../openvidu-coturn/discover-internal-ip.sh | 105 - .../openvidu-coturn/docker-entrypoint.sh | 24 - .../openvidu-deployment-tester/.dockerignore | 3 - .../openvidu-deployment-tester/.gitignore | 3 - .../openvidu-deployment-tester/Dockerfile | 51 - .../openvidu-deployment-tester/README.md | 211 - .../create_image.sh | 8 - .../openvidu-deployment-tester/entrypoint.sh | 19 - .../requirements.txt | 6 - .../src/cli_utils.py | 33 - .../openvidu-deployment-tester/src/main.py | 28 - .../openvidu-deployment-tester/src/tests.py | 107 - .../openvidu-deployment-tester/src/utils.py | 222 - .../docker/openvidu-dev/Dockerfile | 41 - .../docker/openvidu-dev/create_image.sh | 17 - openvidu-server/docker/openvidu-dev/kms.sh | 24 - .../docker/openvidu-dev/supervisord.conf | 13 - .../docker/openvidu-elasticsearch/.gitignore | 1 - .../openvidu-elasticsearch/create_image.sh | 22 - .../elasticsearch_7.6.2_patch_log4j.diff | 25 - .../elasticsearch_7.8.0_patch_log4j.diff | 25 - .../docker/openvidu-proxy/Dockerfile | 33 - .../docker/openvidu-proxy/create_image.sh | 12 - .../docker/openvidu-proxy/default.conf | 8 - .../default_nginx_conf/ce/default.conf | 60 - .../enterprise-ha/default.conf | 103 - .../default_nginx_conf/global/app_config.conf | 4 - .../global/app_config_default.conf | 2 - .../global/app_upstream.conf | 4 - .../global/ce/common_api_ce.conf | 14 - .../global/ce/deprecated_api_ce.conf | 40 - .../global/ce/new_api_ce.conf | 33 - .../global/ce/redirect_www.conf | 15 - .../global/enterprise-ha/app_upstream.conf | 4 - .../enterprise-ha/common_api_enterprise.conf | 35 - .../dashboard_inspector_rules_enterprise.conf | 13 - .../global/nginx_status.conf | 5 - .../global/pro/common_api_pro.conf | 33 - .../pro/dashboard_inspector_rules_pro.conf | 12 - .../global/pro/deprecated_api_pro.conf | 59 - .../global/pro/new_api_pro.conf | 51 - .../global/pro/redirect_www.conf | 3 - .../global/proxy_config.conf | 13 - .../global/redirect_www_ssl.conf | 18 - .../default_nginx_conf/global/ssl_config.conf | 15 - .../global/xframe_sameorigin.conf | 1 - .../default_nginx_conf/pro/default.conf | 85 - .../docker/openvidu-proxy/entrypoint.sh | 523 - .../docker/openvidu-proxy/nginx.conf | 36 - .../update_enterprise_ha_nodes.sh | 26 - .../docker/openvidu-recording/Dockerfile | 42 - .../docker/openvidu-recording/create_image.sh | 10 - .../docker/openvidu-recording/entrypoint.sh | 21 - .../openvidu-recording/firefox/Dockerfile | 37 - .../firefox/configuration/autoconfig.js | 2 - .../firefox/configuration/customconfig.cfg | 10 - .../configuration/forcefull@shdon.com.xpi | Bin 11746 -> 0 bytes ...{d320c473-63c2-47ab-87f8-693b1badb5e3}.xpi | Bin 5987 -> 0 bytes .../firefox/create_image.sh | 1 - .../openvidu-recording/firefox/entrypoint.sh | 99 - .../openvidu-recording/scripts/broadcast.sh | 47 - .../openvidu-recording/scripts/composed.sh | 102 - .../scripts/composed_quick_start.sh | 159 - .../utils/headless-chrome.sh | 53 - .../openvidu-recording/utils/xvfb-run-safe | 21 - .../docker/openvidu-server-pro/Dockerfile | 30 - .../openvidu-server-pro/create_image.sh | 11 - .../docker/openvidu-server-pro/entrypoint.sh | 92 - .../docker/openvidu-server/Dockerfile | 25 - .../docker/openvidu-server/create_image.sh | 23 - .../docker/openvidu-server/entrypoint.sh | 53 - .../docker/utils/coturn-shared-key.template | 17 - .../docker/utils/discover_my_public_ip.sh | 133 - openvidu-server/pom.xml | 285 - openvidu-server/sonar-project.properties | 13 - openvidu-server/src/dashboard/.editorconfig | 13 - openvidu-server/src/dashboard/.gitignore | 43 - openvidu-server/src/dashboard/README.md | 28 - openvidu-server/src/dashboard/angular.json | 119 - .../src/dashboard/e2e/app.e2e-spec.ts | 14 - openvidu-server/src/dashboard/e2e/app.po.ts | 11 - .../src/dashboard/e2e/tsconfig.e2e.json | 12 - openvidu-server/src/dashboard/karma.conf.js | 44 - .../src/dashboard/package-lock.json | 24650 ---------------- openvidu-server/src/dashboard/package.json | 54 - .../src/dashboard/protractor.conf.js | 30 - .../src/dashboard/src/app/app.component.css | 0 .../src/dashboard/src/app/app.component.html | 3 - .../dashboard/src/app/app.component.spec.ts | 32 - .../src/dashboard/src/app/app.component.ts | 10 - .../dashboard/src/app/app.material.module.ts | 42 - .../src/dashboard/src/app/app.module.ts | 41 - .../src/dashboard/src/app/app.routing.ts | 44 - .../dashboard/credentials-dialog.component.ts | 60 - .../dashboard/dashboard.component.css | 293 - .../dashboard/dashboard.component.html | 61 - .../dashboard/dashboard.component.spec.ts | 25 - .../dashboard/dashboard.component.ts | 205 - .../layout-base/layout-base.component.css | 796 - .../layout-base/layout-base.component.html | 7 - .../layout-base/layout-base.component.spec.ts | 25 - .../layout-base/layout-base.component.ts | 153 - .../layout-best-fit.component.ts | 27 - ...ayout-horizontal-presentation.component.ts | 34 - .../layout-vertical-presentation.component.ts | 34 - .../app/components/layouts/openvidu-layout.ts | 377 - .../components/layouts/ov-video.component.ts | 26 - .../session-details.component.css | 0 .../session-details.component.html | 3 - .../session-details.component.spec.ts | 25 - .../session-details.component.ts | 15 - .../src/app/services/info.service.spec.ts | 15 - .../src/app/services/info.service.ts | 23 - .../src/app/services/rest.service.ts | 83 - .../src/dashboard/src/assets/.gitkeep | 0 .../dashboard/src/environments/environment.ts | 8 - openvidu-server/src/dashboard/src/favicon.ico | Bin 5430 -> 0 bytes openvidu-server/src/dashboard/src/index.html | 19 - openvidu-server/src/dashboard/src/main.ts | 11 - .../src/dashboard/src/polyfills.ts | 55 - openvidu-server/src/dashboard/src/styles.css | 26 - openvidu-server/src/dashboard/src/test.ts | 29 - .../src/dashboard/src/tsconfig.app.json | 12 - .../src/dashboard/src/tsconfig.spec.json | 19 - .../src/dashboard/src/typings.d.ts | 5 - openvidu-server/src/dashboard/tsconfig.json | 21 - openvidu-server/src/dashboard/tslint.json | 118 - .../io/openvidu/server/OpenViduServer.java | 395 - .../server/broadcast/BroadcastManager.java | 13 - .../broadcast/BroadcastManagerDummy.java | 18 - .../java/io/openvidu/server/cdr/CDREvent.java | 71 - .../io/openvidu/server/cdr/CDREventEnd.java | 69 - .../server/cdr/CDREventFilterEvent.java | 46 - .../io/openvidu/server/cdr/CDREventName.java | 26 - .../server/cdr/CDREventParticipant.java | 61 - .../cdr/CDREventRecordingStatusChanged.java | 67 - .../openvidu/server/cdr/CDREventSession.java | 45 - .../openvidu/server/cdr/CDREventSignal.java | 53 - .../server/cdr/CDREventWebrtcConnection.java | 109 - .../io/openvidu/server/cdr/CDRLogger.java | 33 - .../io/openvidu/server/cdr/CDRLoggerFile.java | 50 - .../openvidu/server/cdr/CallDetailRecord.java | 208 - .../openvidu/server/cdr/WebrtcDebugEvent.java | 55 - .../io/openvidu/server/config/Dotenv.java | 155 - .../config/HttpHandshakeInterceptor.java | 52 - .../openvidu/server/config/InfoHandler.java | 70 - .../server/config/InfoSocketConfig.java | 50 - .../server/config/OpenviduBuildInfo.java | 41 - .../server/config/OpenviduConfig.java | 1406 - .../server/config/SecurityConfig.java | 96 - .../io/openvidu/server/core/EndReason.java | 139 - .../io/openvidu/server/core/FinalUser.java | 89 - .../server/core/IdentifierPrefixes.java | 17 - .../io/openvidu/server/core/MediaOptions.java | 104 - .../io/openvidu/server/core/MediaServer.java | 30 - .../io/openvidu/server/core/Participant.java | 355 - .../java/io/openvidu/server/core/Session.java | 285 - .../server/core/SessionEventsHandler.java | 711 - .../server/core/SessionInterface.java | 54 - .../openvidu/server/core/SessionManager.java | 735 - .../java/io/openvidu/server/core/Token.java | 251 - .../openvidu/server/core/TokenGenerator.java | 64 - .../openvidu/server/core/TokenRegister.java | 93 - .../coturn/CoturnCredentialsService.java | 45 - .../server/coturn/TurnCredentials.java | 38 - .../kurento/core/KurentoMediaOptions.java | 84 - .../kurento/core/KurentoParticipant.java | 679 - .../KurentoParticipantEndpointConfig.java | 236 - .../server/kurento/core/KurentoSession.java | 393 - .../core/KurentoSessionEventsHandler.java | 62 - .../kurento/core/KurentoSessionManager.java | 1454 - .../server/kurento/endpoint/EndpointType.java | 7 - .../server/kurento/endpoint/KmsEvent.java | 71 - .../kurento/endpoint/KmsMediaEvent.java | 44 - .../kurento/endpoint/KurentoFilter.java | 84 - .../kurento/endpoint/MediaEndpoint.java | 741 - .../kurento/endpoint/PublisherEndpoint.java | 616 - .../server/kurento/endpoint/SdpType.java | 22 - .../kurento/endpoint/SubscriberEndpoint.java | 107 - .../server/kurento/endpoint/TrackType.java | 22 - .../server/kurento/kms/DummyLoadManager.java | 27 - .../kurento/kms/FixedOneKmsManager.java | 131 - .../io/openvidu/server/kurento/kms/Kms.java | 354 - .../server/kurento/kms/KmsManager.java | 485 - .../server/kurento/kms/KmsProperties.java | 37 - .../server/kurento/kms/LoadManager.java | 24 - .../server/recording/CompositeWrapper.java | 193 - .../recording/DummyRecordingDownloader.java | 36 - .../recording/DummyRecordingUploader.java | 20 - .../recording/RecorderEndpointWrapper.java | 174 - .../openvidu/server/recording/Recording.java | 239 - .../server/recording/RecordingDownloader.java | 30 - .../server/recording/RecordingInfoUtils.java | 129 - .../recording/RecordingNotification.java | 48 - .../server/recording/RecordingUploader.java | 11 - .../ComposedQuickStartRecordingService.java | 306 - .../service/ComposedRecordingService.java | 467 - .../recording/service/RecordingManager.java | 952 - .../service/RecordingManagerUtils.java | 61 - .../RecordingManagerUtilsLocalStorage.java | 64 - .../recording/service/RecordingService.java | 250 - .../service/SingleStreamRecordingService.java | 587 - .../WaitForContainerStoppedCallback.java | 62 - .../server/resources/CDRHttpHandler.java | 40 - .../resources/FrontendResourceHandler.java | 51 - ...RecordingCustomLayoutsResourceHandler.java | 49 - .../resources/RecordingsResourceHandler.java | 46 - .../server/rest/ApiRestPathRewriteFilter.java | 145 - .../server/rest/CDRRestController.java | 79 - .../rest/CertificateRestController.java | 36 - .../server/rest/ConfigRestController.java | 158 - .../openvidu/server/rest/RequestMappings.java | 23 - .../server/rest/SessionRestController.java | 905 - .../io/openvidu/server/rpc/RpcConnection.java | 89 - .../io/openvidu/server/rpc/RpcHandler.java | 1009 - .../server/rpc/RpcNotificationService.java | 218 - .../server/summary/ParticipantSummary.java | 113 - .../server/summary/SessionSummary.java | 92 - .../server/utils/CommandExecutor.java | 115 - .../server/utils/CustomFileManager.java | 128 - .../openvidu/server/utils/DockerManager.java | 28 - .../io/openvidu/server/utils/GeoLocation.java | 63 - .../server/utils/GeoLocationByIp.java | 26 - .../server/utils/GeoLocationByIpDummy.java | 12 - .../io/openvidu/server/utils/JsonUtils.java | 83 - .../server/utils/LocalCustomFileManager.java | 41 - .../server/utils/LocalDockerManager.java | 309 - .../server/utils/MediaNodeManager.java | 25 - .../server/utils/MediaNodeManagerDummy.java | 47 - .../openvidu/server/utils/RecordingUtils.java | 18 - .../server/utils/RemoteOperationUtils.java | 21 - .../io/openvidu/server/utils/RestUtils.java | 29 - .../io/openvidu/server/utils/SDPMunging.java | 225 - .../server/utils/UpdatableTimerTask.java | 78 - .../server/utils/VersionComparator.java | 70 - .../utils/ice/IceCandidateDataParser.java | 111 - .../server/utils/ice/IceCandidateType.java | 5 - .../server/webhook/CDRLoggerWebhook.java | 56 - .../server/webhook/HttpWebhookSender.java | 198 - ...itional-spring-configuration-metadata.json | 248 - .../OpenVidu.postman_collection.json | 1120 - .../application-container.properties | 6 - .../src/main/resources/application.properties | 53 - openvidu-server/src/main/resources/banner.txt | 11 - .../src/main/resources/logback-spring.xml | 66 - .../main/resources/openvidu-selfsigned.p12 | Bin 2876 -> 0 bytes .../src/main/resources/static/index.html | 31 - .../main/resources/templates/accept-cert.html | 9 - .../main/resources/templates/error/404.html | 50 - ...essionGarbageCollectorIntegrationTest.java | 129 - .../integration/WebhookIntegrationTest.java | 230 - .../config/IntegrationTestConfiguration.java | 92 - .../openvidu/server/test/unit/DotenvTest.java | 86 - .../test/unit/IceServerPropertiesTest.java | 174 - .../server/test/unit/SDPMungingTest.java | 162 - .../test/unit/VersionComparatorTest.java | 94 - openvidu-server/src/test/resources/.env | 5 - .../src/test/resources/application.properties | 8 - .../resources/integration-test.properties | 44 - .../src/test/resources/log4j.properties | 21 - .../src/test/resources/sdp/sdp_chrome84.txt | 143 - .../src/test/resources/sdp/sdp_firefox79.txt | 67 - .../test/resources/sdp/sdp_kurento_h264.txt | 61 - .../src/test/resources/sdp/sdp_safari13-1.txt | 107 - .../test/browsers/AndroidAppUser.java | 2 +- .../test/browsers/AndroidChromeUser.java | 3 - .../openvidu/test/browsers/BrowserUser.java | 25 +- .../io/openvidu/test/browsers/ChromeUser.java | 14 +- .../io/openvidu/test/browsers/EdgeUser.java | 8 +- .../openvidu/test/browsers/FirefoxUser.java | 8 +- .../browsers/utils/CommandLineExecutor.java | 9 +- .../browsers/utils/webhook/CustomWebhook.java | 4 +- openvidu-test-e2e/docker/Dockerfile | 8 +- openvidu-test-e2e/pom.xml | 5 + .../test/e2e/OpenViduEventManager.java | 116 +- .../io/openvidu/test/e2e/OpenViduTestE2e.java | 642 +- .../e2e/AbstractOpenViduTestappE2eTest.java | 31 +- .../test/e2e/OpenViduMobileE2eTest.java | 450 - .../test/e2e/OpenViduProTestAppE2eTest.java | 3517 --- .../test/e2e/OpenViduTestAppE2eTest.java | 5215 +--- .../test/e2e/annotations/OnlyKurento.java | 15 - openvidu-testapp/.editorconfig | 5 +- openvidu-testapp/.gitignore | 27 +- openvidu-testapp/.vscode/extensions.json | 4 + openvidu-testapp/.vscode/launch.json | 20 + openvidu-testapp/.vscode/tasks.json | 42 + openvidu-testapp/README.md | 32 +- openvidu-testapp/angular.json | 125 +- openvidu-testapp/cert.pem | 20 - openvidu-testapp/karma.conf.js | 33 - openvidu-testapp/key.pem | 28 - openvidu-testapp/package-lock.json | 19864 +++++-------- openvidu-testapp/package.json | 93 +- .../src/app/app-routing.module.ts | 10 + openvidu-testapp/src/app/app.component.css | 23 +- openvidu-testapp/src/app/app.component.html | 41 +- .../src/app/app.component.spec.ts | 38 +- openvidu-testapp/src/app/app.component.ts | 40 +- .../src/app/app.material.module.ts | 50 - openvidu-testapp/src/app/app.module.ts | 112 +- openvidu-testapp/src/app/app.routing.ts | 31 - .../audio-track/audio-track.component.css | 43 + .../audio-track/audio-track.component.html | 16 + .../audio-track/audio-track.component.spec.ts | 21 + .../audio-track/audio-track.component.ts | 60 + .../events-dialog/events-dialog.component.ts | 46 +- .../extension-dialog.component.ts | 36 - .../local-recording-dialog.component.ts | 65 - ...her-stream-operations-dialog.component.css | 36 - ...er-stream-operations-dialog.component.html | 62 - ...ther-stream-operations-dialog.component.ts | 124 - .../publisher-properties-dialog.component.css | 35 - ...publisher-properties-dialog.component.html | 56 - .../publisher-properties-dialog.component.ts | 128 - .../recording-properties.component.css | 42 - .../recording-properties.component.html | 53 - .../recording-properties.component.ts | 24 - .../room-api-dialog.component.css | 123 + .../room-api-dialog.component.html | 202 + .../room-api-dialog.component.ts | 297 + .../room-options-dialog.component.css | 0 .../room-options-dialog.component.html | 8 + .../room-options-dialog.component.spec.ts | 21 + .../room-options-dialog.component.ts | 18 + .../scenario-properties-dialog.component.css | 73 - .../scenario-properties-dialog.component.html | 61 - .../scenario-properties-dialog.component.ts | 109 - .../session-api-dialog.component.css | 51 - .../session-api-dialog.component.html | 127 - .../session-api-dialog.component.ts | 311 - .../session-info-dialog.component.ts | 38 - .../session-properties-dialog.component.css | 56 - .../session-properties-dialog.component.html | 139 - .../session-properties-dialog.component.ts | 50 - .../show-codec-dialog.component.ts | 26 - .../show-configured-ice.component.ts | 37 - .../openvidu-instance.component.css | 223 +- .../openvidu-instance.component.html | 233 +- .../openvidu-instance.component.spec.ts | 12 +- .../openvidu-instance.component.ts | 1718 +- .../participant/participant.component.css | 101 + .../participant/participant.component.html | 59 + .../participant/participant.component.spec.ts | 21 + .../participant/participant.component.ts | 345 + .../test-apirest/test-apirest.component.css | 23 - .../test-apirest/test-apirest.component.html | 108 - .../test-apirest.component.spec.ts | 25 - .../test-apirest/test-apirest.component.ts | 123 - .../test-scenarios.component.css | 32 - .../test-scenarios.component.html | 34 - .../test-scenarios.component.spec.ts | 25 - .../test-scenarios.component.ts | 521 - .../test-sessions/test-sessions.component.css | 23 +- .../test-sessions.component.html | 46 +- .../test-sessions.component.spec.ts | 12 +- .../test-sessions/test-sessions.component.ts | 67 +- .../app/components/track/track.component.ts | 66 + .../users-table/table-video.component.ts | 87 - .../users-table/users-table.component.css | 28 - .../users-table/users-table.component.html | 24 - .../users-table/users-table.component.ts | 37 - .../video-track/video-track.component.css | 45 + .../video-track/video-track.component.html | 19 + .../video-track/video-track.component.spec.ts | 21 + .../video-track/video-track.component.ts | 71 + .../components/video/ov-video.component.ts | 38 - .../app/components/video/video.component.css | 103 - .../app/components/video/video.component.html | 99 - .../app/components/video/video.component.ts | 882 - .../services/livekit-params.service.spec.ts | 16 + .../app/services/livekit-params.service.ts | 30 + .../app/services/mute-subscribers.service.ts | 21 - .../services/openvidu-params.service.spec.ts | 15 - .../app/services/openvidu-params.service.ts | 31 - .../services/openvidu-rest.service.spec.ts | 15 - .../src/app/services/openvidu-rest.service.ts | 58 - .../src/app/services/room-api.service.ts | 202 + .../app/services/test-feed.service.spec.ts | 15 - .../src/app/services/test-feed.service.ts | 37 +- openvidu-testapp/src/assets/.npmignore | 0 .../fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2 | Bin 49028 -> 0 bytes .../openvidu_globe_bg_transp_cropped.png | Bin 11746 -> 0 bytes .../openvidu_grey_bg_transp_cropped.png | Bin 39532 -> 0 bytes .../images/openvidu_vert_white_bg_trans.png | Bin 26577 -> 0 bytes openvidu-testapp/src/assets/images/volume.png | Bin 986 -> 0 bytes .../src/environments/environment.prod.ts | 3 - .../src/environments/environment.ts | 10 - openvidu-testapp/src/favicon.ico | Bin 5430 -> 948 bytes openvidu-testapp/src/index.html | 17 +- openvidu-testapp/src/main.ts | 7 +- openvidu-testapp/src/material-icons.css | 23 - openvidu-testapp/src/openvidu-theme.scss | 33 +- openvidu-testapp/src/polyfills.ts | 8 + openvidu-testapp/src/styles.css | 132 +- openvidu-testapp/src/test.ts | 32 - openvidu-testapp/src/tsconfig.app.json | 13 - openvidu-testapp/src/tsconfig.spec.json | 21 - openvidu-testapp/src/typings.d.ts | 5 - openvidu-testapp/tsconfig.app.json | 15 + openvidu-testapp/tsconfig.json | 44 +- openvidu-testapp/tsconfig.spec.json | 14 + openvidu-testapp/tslint.json | 141 - 1002 files changed, 32871 insertions(+), 145337 deletions(-) delete mode 100644 ci-scripts/commons/build.sh delete mode 100644 ci-scripts/commons/bump.sh delete mode 100644 ci-scripts/commons/test-utils.sh delete mode 100644 ci-scripts/kurento-snapshots.xml delete mode 100755 ci-scripts/openvidu-e2e-tests.sh delete mode 100644 openvidu-browser/.gitignore delete mode 100644 openvidu-browser/.npmignore delete mode 100644 openvidu-browser/.prettierrc delete mode 100644 openvidu-browser/LICENSE delete mode 100755 openvidu-browser/config/replace_for_ts44.sh delete mode 100644 openvidu-browser/config/tsconfig.json delete mode 100644 openvidu-browser/config/tslint.json delete mode 100755 openvidu-browser/generate-docs.sh delete mode 100644 openvidu-browser/package-lock.json delete mode 100644 openvidu-browser/package.json delete mode 100644 openvidu-browser/src/Main.ts delete mode 100644 openvidu-browser/src/OpenVidu/Connection.ts delete mode 100644 openvidu-browser/src/OpenVidu/EventDispatcher.ts delete mode 100644 openvidu-browser/src/OpenVidu/Filter.ts delete mode 100644 openvidu-browser/src/OpenVidu/LocalRecorder.ts delete mode 100644 openvidu-browser/src/OpenVidu/OpenVidu.ts delete mode 100644 openvidu-browser/src/OpenVidu/Publisher.ts delete mode 100644 openvidu-browser/src/OpenVidu/Session.ts delete mode 100644 openvidu-browser/src/OpenVidu/Stream.ts delete mode 100644 openvidu-browser/src/OpenVidu/StreamManager.ts delete mode 100644 openvidu-browser/src/OpenVidu/Subscriber.ts delete mode 100644 openvidu-browser/src/OpenVidu/tsconfig.json delete mode 100644 openvidu-browser/src/OpenViduInternal/Enums/LocalRecorderState.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Enums/OpenViduError.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Enums/TypeOfVideo.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Enums/VideoInsertMode.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/ConnectionEvent.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/ConnectionPropertyChangedEvent.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/Event.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/EventMap/EventMap.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/EventMap/PublisherEventMap.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/EventMap/SessionEventMap.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/EventMap/StreamManagerEventMap.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/ExceptionEvent.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/FilterEvent.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/NetworkQualityLevelChangedEvent.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/PublisherSpeakingEvent.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/RecordingEvent.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/SessionDisconnectedEvent.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/SignalEvent.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/SpeechToTextEvent.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/StreamEvent.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/StreamManagerEvent.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/StreamPropertyChangedEvent.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/Types/Types.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Events/VideoElementEvent.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Private/CustomMediaStreamConstraints.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Private/IceServerProperties.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Private/InboundStreamOptions.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Private/LocalConnectionOptions.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Private/OutboundStreamOptions.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Private/RemoteConnectionOptions.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Private/SessionOptions.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Private/SignalOptions.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Private/StreamOptionsServer.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Public/Capabilities.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Public/Device.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Public/PublisherProperties.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Public/SignalOptions.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Public/StreamManagerVideo.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Public/SubscriberProperties.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/Mapper.js delete mode 100644 openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/index.js delete mode 100644 openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/jsonrpcclient.js delete mode 100644 openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/transports/index.js delete mode 100644 openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/transports/webSocketWithReconnection.js delete mode 100644 openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/index.js delete mode 100644 openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/packers/JsonRPC.js delete mode 100644 openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/packers/XmlRPC.js delete mode 100644 openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/packers/index.js delete mode 100644 openvidu-browser/src/OpenViduInternal/Logger/ConsoleLogger.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Logger/OpenViduLogger.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/Logger/OpenViduLoggerConfiguration.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/ScreenSharing/Screen-Capturing-Auto.js delete mode 100644 openvidu-browser/src/OpenViduInternal/ScreenSharing/Screen-Capturing.js delete mode 100644 openvidu-browser/src/OpenViduInternal/Utils/Platform.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts delete mode 100644 openvidu-browser/src/OpenViduInternal/WebRtcStats/WebRtcStats.ts delete mode 100644 openvidu-browser/src/index.ts delete mode 100644 openvidu-browser/tsconfig.json delete mode 100644 openvidu-client/.gitignore delete mode 100644 openvidu-client/README.md delete mode 100644 openvidu-client/pom.xml delete mode 100644 openvidu-client/src/main/java/io/openvidu/client/OpenViduClient.java delete mode 100644 openvidu-client/src/main/java/io/openvidu/client/OpenViduException.java delete mode 100644 openvidu-client/src/main/java/io/openvidu/client/ServerJsonRpcHandler.java delete mode 100644 openvidu-client/src/main/java/io/openvidu/client/internal/IceCandidate.java delete mode 100644 openvidu-client/src/main/java/io/openvidu/client/internal/IceCandidateInfo.java delete mode 100644 openvidu-client/src/main/java/io/openvidu/client/internal/JsonRoomUtils.java delete mode 100644 openvidu-client/src/main/java/io/openvidu/client/internal/MediaErrorInfo.java delete mode 100644 openvidu-client/src/main/java/io/openvidu/client/internal/Notification.java delete mode 100644 openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantEvictedInfo.java delete mode 100644 openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantJoinedInfo.java delete mode 100644 openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantLeftInfo.java delete mode 100644 openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantPublishedInfo.java delete mode 100644 openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantUnpublishedInfo.java delete mode 100644 openvidu-client/src/main/java/io/openvidu/client/internal/ProtocolElements.java delete mode 100644 openvidu-client/src/main/java/io/openvidu/client/internal/RoomClosedInfo.java delete mode 100644 openvidu-client/src/main/java/io/openvidu/client/internal/SendMessageInfo.java delete mode 100644 openvidu-client/src/test/java/io/openvidu/client/test/OpenViduClientTest.java create mode 100644 openvidu-components-angular/.prettierignore delete mode 100644 openvidu-components-angular/docs.md create mode 100644 openvidu-components-angular/e2e/webcomponent-e2e/api-directives.test.ts create mode 100644 openvidu-components-angular/e2e/webcomponent-e2e/captions.test.ts create mode 100644 openvidu-components-angular/e2e/webcomponent-e2e/chat.test.ts create mode 100644 openvidu-components-angular/e2e/webcomponent-e2e/events.test.ts create mode 100644 openvidu-components-angular/e2e/webcomponent-e2e/media-devices.test.ts create mode 100644 openvidu-components-angular/e2e/webcomponent-e2e/panels.test.ts create mode 100644 openvidu-components-angular/e2e/webcomponent-e2e/screensharing.test.ts create mode 100644 openvidu-components-angular/e2e/webcomponent-e2e/stream.test.ts create mode 100644 openvidu-components-angular/e2e/webcomponent-e2e/toolbar.test.ts create mode 100644 openvidu-components-angular/migration-guide.md delete mode 100644 openvidu-components-angular/projects/openvidu-angular/README.md delete mode 100644 openvidu-components-angular/projects/openvidu-angular/doc/.compodocrc.json delete mode 100644 openvidu-components-angular/projects/openvidu-angular/package-lock.json delete mode 100644 openvidu-components-angular/projects/openvidu-angular/package.json delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/admin/dashboard/dashboard.component.html delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/admin/dashboard/dashboard.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/admin/login/login.component.css delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/admin/login/login.component.html delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/admin/login/login.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.html delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/dialogs/recording-dialog.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/layout/layout.component.html delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/layout/layout.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/activities-panel.component.css delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/activities-panel.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.css delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/panel.component.spec.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.css delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/participants-panel/participants-panel/participants-panel.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/settings-panel/settings-panel.component.css delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/pre-join/pre-join.component.css delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/pre-join/pre-join.component.html delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/pre-join/pre-join.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/session/session.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.css delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.html delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/captions/captions.component.css delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.css delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.html delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/nickname-input/nickname-input.component.css delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/nickname-input/nickname-input.component.html delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/nickname-input/nickname-input.component.spec.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/nickname-input/nickname-input.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.css delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.html delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/stream/stream.component.css delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/stream/stream.component.html delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/stream/stream.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/toolbar/toolbar.component.css delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/video/video.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.css delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/config/custom-cdk-overlay.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/config/custom-flexlayout-breakpoints.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/config/openvidu-angular.config.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/broadcasting-activity.directive.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/recording-activity.directive.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/stream.directive.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/directives/template/openvidu-angular.directive.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/matchers/nickname.matcher.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/models/broadcasting.model.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/models/icon.model.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/models/panel.model.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/models/participant.model.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/models/recording.model.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/models/signal.model.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/models/token.model.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/models/video-type.model.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/pipes/participant.pipe.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/services/broadcasting/broadcasting.service.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/services/chat/chat.service.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/services/config/openvidu-angular.config.service.spec.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/services/config/openvidu-angular.config.service.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/services/openvidu/openvidu.service.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/services/participant/participant.service.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/src/lib/services/recording/recording.service.ts delete mode 100644 openvidu-components-angular/projects/openvidu-angular/tsconfig.lib.json rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/.browserslistrc (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/.prettierrc (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/README.md create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/doc/.compodocrc.json rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/doc/favicon.ico (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/doc/scripts/generate-directive-tables.js create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/doc/scripts/generate-directive-tutorials.js rename openvidu-components-angular/projects/{openvidu-angular/doc => openvidu-components-angular/doc/styles}/style.css (68%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/doc/tsconfig.doc.json (72%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/karma.conf.js (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/ng-package.json (69%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/package.json create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.html create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.md rename openvidu-components-angular/projects/{openvidu-angular/src/lib/admin/dashboard/dashboard.component.css => openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.scss} (66%) rename openvidu-components-angular/projects/{openvidu-angular/src/lib/admin/dashboard/dashboard.component.spec.ts => openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.spec.ts} (58%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.ts create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.html create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.md create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.scss rename openvidu-components-angular/projects/{openvidu-angular/src/lib/admin/login/login.component.spec.ts => openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.spec.ts} (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.ts rename openvidu-components-angular/projects/{openvidu-angular/src/lib/components/audio-wave/audio-wave.component.css => openvidu-components-angular/src/lib/components/audio-wave/audio-wave.component.scss} (94%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/audio-wave/audio-wave.component.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/audio-wave/audio-wave.component.ts rename openvidu-components-angular/projects/{openvidu-angular/src/lib/components/avatar-profile/avatar-profile.component.css => openvidu-components-angular/src/lib/components/avatar-profile/avatar-profile.component.scss} (89%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/avatar-profile/avatar-profile.component.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/avatar-profile/avatar-profile.component.ts (79%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/captions/captions.component.html (100%) rename openvidu-components-angular/projects/{openvidu-angular/src/lib/components/captions/captions.component.css => openvidu-components-angular/src/lib/components/captions/captions.component.scss} (87%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/captions/captions.component.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/captions/captions.component.ts (67%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/dialogs/delete-recording.component.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/dialogs/dialog.component.ts (89%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/dialogs/pro-feature-dialog.component.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/dialogs/recording-dialog.component.ts create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.html create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.md rename openvidu-components-angular/projects/{openvidu-angular/src/lib/components/layout/layout.component.css => openvidu-components-angular/src/lib/components/layout/layout.component.scss} (50%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/layout/layout.component.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.ts rename openvidu-components-angular/projects/{openvidu-angular/src/lib/components/video/video.component.css => openvidu-components-angular/src/lib/components/media-element/media-element.component.scss} (100%) rename openvidu-components-angular/projects/{openvidu-angular/src/lib/components/video/video.component.spec.ts => openvidu-components-angular/src/lib/components/media-element/media-element.component.spec.ts} (90%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/media-element/media-element.component.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/activities-panel/activities-panel.component.html (60%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.md create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.scss rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/activities-panel/activities-panel.component.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.html (94%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.md rename openvidu-components-angular/projects/{openvidu-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.css => openvidu-components-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.scss} (97%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.ts (50%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.html (88%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.md create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.scss rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/background-effects-panel/background-effects-panel.component.html (58%) rename openvidu-components-angular/projects/{openvidu-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.css => openvidu-components-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.scss} (95%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/background-effects-panel/background-effects-panel.component.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/chat-panel/chat-panel.component.html (58%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/chat-panel/chat-panel.component.md rename openvidu-components-angular/projects/{openvidu-angular/src/lib/components/panel/chat-panel/chat-panel.component.css => openvidu-components-angular/src/lib/components/panel/chat-panel/chat-panel.component.scss} (69%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/chat-panel/chat-panel.component.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/chat-panel/chat-panel.component.ts (58%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/panel.component.html (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/panel.component.md rename openvidu-components-angular/projects/{openvidu-angular/src/lib/components/panel/panel.component.css => openvidu-components-angular/src/lib/components/panel/panel.component.scss} (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/panel.component.ts (52%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.html (77%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.md create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.scss rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/participants-panel/participants-panel/participants-panel.component.html (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/participants-panel/participants-panel/participants-panel.component.md rename openvidu-components-angular/projects/{openvidu-angular/src/lib/components/panel/participants-panel/participants-panel/participants-panel.component.css => openvidu-components-angular/src/lib/components/panel/participants-panel/participants-panel/participants-panel.component.scss} (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/participants-panel/participants-panel/participants-panel.component.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/participants-panel/participants-panel/participants-panel.component.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/settings-panel/settings-panel.component.html (70%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/settings-panel/settings-panel.component.scss rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/settings-panel/settings-panel.component.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/panel/settings-panel/settings-panel.component.ts (51%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.html create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.scss rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/pre-join/pre-join.component.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/session/session.component.html (77%) rename openvidu-components-angular/projects/{openvidu-angular/src/lib/components/session/session.component.css => openvidu-components-angular/src/lib/components/session/session.component.scss} (94%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/session/session.component.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.ts create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/audio-devices/audio-devices.component.html create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/audio-devices/audio-devices.component.scss rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/settings/audio-devices/audio-devices.component.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/audio-devices/audio-devices.component.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/settings/captions/captions.component.html (99%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/captions/captions.component.scss rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/settings/captions/captions.component.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/settings/captions/captions.component.ts (74%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/lang-selector/lang-selector.component.html create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/lang-selector/lang-selector.component.scss rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/settings/lang-selector/lang-selector.component.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/settings/lang-selector/lang-selector.component.ts (63%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/participant-name-input/participant-name-input.component.html create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/participant-name-input/participant-name-input.component.scss create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/participant-name-input/participant-name-input.component.spec.ts create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/participant-name-input/participant-name-input.component.ts create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/video-devices/video-devices.component.html create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/video-devices/video-devices.component.scss rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/settings/video-devices/video-devices.component.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/video-devices/video-devices.component.ts create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.html create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.md create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.scss rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/stream/stream.component.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/toolbar/toolbar.component.html (61%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar.component.md create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar.component.scss rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/toolbar/toolbar.component.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/toolbar/toolbar.component.ts (55%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/videoconference/videoconference.component.html (56%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/videoconference/videoconference.component.md create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/videoconference/videoconference.component.scss rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/components/videoconference/videoconference.component.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/videoconference/videoconference.component.ts create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/config/custom-cdk-overlay.ts create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/config/openvidu-components-angular.config.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/directives/api/activities-panel.directive.ts (87%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/directives/api/admin.directive.ts (70%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/directives/api/api.directive.module.ts (60%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/directives/api/internals.directive.ts (99%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/directives/api/participant-panel-item.directive.ts (83%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/stream.directive.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/directives/api/toolbar.directive.ts (76%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/directives/api/videoconference.directive.ts (58%) rename openvidu-components-angular/projects/{openvidu-angular/src/lib/directives/template/openvidu-angular.directive.module.ts => openvidu-components-angular/src/lib/directives/template/openvidu-components-angular.directive.module.ts} (86%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/template/openvidu-components-angular.directive.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/lang/cn.json (70%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/lang/de.json (71%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/lang/en.json (71%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/lang/es.json (70%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/lang/fr.json (69%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/lang/hi.json (71%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/lang/it.json (70%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/lang/ja.json (69%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/lang/nl.json (71%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/lang/pt.json (70%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/models/background-effect.model.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/models/broadcasting.model.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/models/caption.model.ts (62%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/models/chat.model.ts (77%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/models/data-topic.model.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/models/device.model.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/models/dialog.model.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/models/lang.model.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/models/layout.model.ts (88%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/models/linkifier.model.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/models/logger.model.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/models/notification-options.model.ts (88%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/models/openvidu.model.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/models/panel.model.ts create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/models/participant.model.ts create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/models/recording.model.ts create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/models/room.model.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/models/storage.model.ts (62%) rename openvidu-components-angular/projects/{openvidu-angular/src/lib/openvidu-angular.material.module.ts => openvidu-components-angular/src/lib/openvidu-components-angular.material.module.ts} (84%) rename openvidu-components-angular/projects/{openvidu-angular/src/lib/openvidu-angular.module.ts => openvidu-components-angular/src/lib/openvidu-components-angular.module.ts} (63%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/pipes/linkify.pipe.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/pipes/participant.pipe.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/pipes/recording.pipe.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/pipes/translate.pipe.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/action/action.service.mock.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/action/action.service.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/action/action.service.ts (58%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/broadcasting/broadcasting.service.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/broadcasting/broadcasting.service.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/caption/caption.service.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/caption/caption.service.ts (92%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/cdk-overlay/cdk-overlay.service.mock.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/cdk-overlay/cdk-overlay.service.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/cdk-overlay/cdk-overlay.service.ts (65%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/chat/chat.service.mock.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/chat/chat.service.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/chat/chat.service.ts rename openvidu-components-angular/projects/{openvidu-angular/src/lib/services/config/openvidu-angular.config.service.mock.ts => openvidu-components-angular/src/lib/services/config/openvidu-components-angular.config.service.mock.ts} (50%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/config/openvidu-components-angular.config.service.spec.ts create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/config/openvidu-components-angular.config.service.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/device/device.service.mock.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/device/device.service.spec.ts (97%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/device/device.service.ts (66%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/document/document.service.mock.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/document/document.service.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/document/document.service.ts (83%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/layout/layout.service.mock.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/layout/layout.service.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/layout/layout.service.ts (99%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/logger/logger.service.mock.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/logger/logger.service.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/logger/logger.service.ts (86%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/openvidu/openvidu.service.mock.ts (97%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/openvidu/openvidu.service.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/openvidu/openvidu.service.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/panel/panel.service.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/panel/panel.service.ts (53%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/participant/participant.service.mock.ts (82%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/participant/participant.service.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/participant/participant.service.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/platform/platform.service.mock.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/platform/platform.service.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/platform/platform.service.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/recording/recording.service.spec.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/recording/recording.service.ts rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/storage/storage.service.mock.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/storage/storage.service.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/storage/storage.service.ts (58%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/translate/translate.service.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/translate/translate.service.ts (91%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/virtual-background/virtual-background.service.spec.ts (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/lib/services/virtual-background/virtual-background.service.ts (53%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/public-api.ts (75%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/src/test.ts (100%) create mode 100644 openvidu-components-angular/projects/openvidu-components-angular/tsconfig.lib.json rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/tsconfig.lib.prod.json (100%) rename openvidu-components-angular/projects/{openvidu-angular => openvidu-components-angular}/tsconfig.spec.json (100%) rename openvidu-server/src/dashboard/src/environments/environment.prod.ts => openvidu-components-angular/src/environments/environment.testing.ts (53%) delete mode 100644 openvidu-components-angular/src/polyfills.ts create mode 100644 openvidu-components-angular/src/proxy.conf.json delete mode 100644 openvidu-components-angular/src/proxy.config.json delete mode 100644 openvidu-components-angular/tslint.json delete mode 100644 openvidu-java-client/README.md delete mode 100755 openvidu-java-client/generate-docs.sh delete mode 100644 openvidu-java-client/pom.xml delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/ConnectionProperties.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/ConnectionType.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/IceServerProperties.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/KurentoOptions.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/MediaMode.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/OpenViduException.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/OpenViduHttpException.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/OpenViduJavaClientException.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/OpenViduRole.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/Publisher.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingLayout.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingMode.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/Utils.java delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/VideoCodec.java delete mode 100644 openvidu-java-client/src/test/java/io/openvidu/java/client/OpenViduHttpExceptionTest.java delete mode 100644 openvidu-java-client/src/test/java/io/openvidu/java/client/OpenViduJavaClientExceptionTest.java delete mode 100644 openvidu-java-client/src/test/java/io/openvidu/java/client/test/ConnectionPropertiesTest.java delete mode 100644 openvidu-java-client/src/test/java/io/openvidu/java/client/test/OpenViduConstructorTest.java delete mode 100644 openvidu-java-client/src/test/java/io/openvidu/java/client/test/RecordingPropertiesTest.java delete mode 100644 openvidu-java-client/src/test/java/io/openvidu/java/client/test/SessionPropertiesTest.java delete mode 100644 openvidu-java-client/src/test/java/io/openvidu/java/client/test/UtilsFormatCheckerTest.java delete mode 100644 openvidu-node-client/.gitignore delete mode 100644 openvidu-node-client/.npmignore delete mode 100644 openvidu-node-client/.prettierrc delete mode 100644 openvidu-node-client/README.md delete mode 100644 openvidu-node-client/config/tsconfig.json delete mode 100644 openvidu-node-client/config/tslint.json delete mode 100644 openvidu-node-client/config/typedoc.js delete mode 100755 openvidu-node-client/generate-docs.sh delete mode 100644 openvidu-node-client/package-lock.json delete mode 100644 openvidu-node-client/package.json delete mode 100644 openvidu-node-client/src/Connection.ts delete mode 100644 openvidu-node-client/src/ConnectionProperties.ts delete mode 100644 openvidu-node-client/src/ConnectionType.ts delete mode 100644 openvidu-node-client/src/IceServerProperties.ts delete mode 100644 openvidu-node-client/src/Logger/ConsoleLogger.ts delete mode 100644 openvidu-node-client/src/Logger/OpenViduLogger.ts delete mode 100644 openvidu-node-client/src/MediaMode.ts delete mode 100644 openvidu-node-client/src/OpenVidu.ts delete mode 100644 openvidu-node-client/src/OpenViduRole.ts delete mode 100644 openvidu-node-client/src/Publisher.ts delete mode 100644 openvidu-node-client/src/Recording.ts delete mode 100644 openvidu-node-client/src/RecordingLayout.ts delete mode 100644 openvidu-node-client/src/RecordingMode.ts delete mode 100644 openvidu-node-client/src/RecordingProperties.ts delete mode 100644 openvidu-node-client/src/Session.ts delete mode 100644 openvidu-node-client/src/SessionProperties.ts delete mode 100644 openvidu-node-client/src/TokenOptions.ts delete mode 100644 openvidu-node-client/src/VideoCodec.ts delete mode 100644 openvidu-node-client/src/index.ts delete mode 100644 openvidu-node-client/tsconfig.json delete mode 100644 openvidu-server/.gitignore delete mode 100644 openvidu-server/LICENSE delete mode 100644 openvidu-server/NOTICE delete mode 100644 openvidu-server/README.md delete mode 100644 openvidu-server/deployments/ce/aws/CF-OpenVidu.yaml.template delete mode 100644 openvidu-server/deployments/ce/aws/cfn-mkt-ov-ce-ami.yaml.template delete mode 100755 openvidu-server/deployments/ce/aws/createAMI.sh delete mode 100644 openvidu-server/deployments/ce/docker-compose/.env delete mode 100644 openvidu-server/deployments/ce/docker-compose/docker-compose.override.yml delete mode 100644 openvidu-server/deployments/ce/docker-compose/docker-compose.yml delete mode 100755 openvidu-server/deployments/ce/docker-compose/install_openvidu.sh delete mode 100755 openvidu-server/deployments/ce/docker-compose/openvidu delete mode 100644 openvidu-server/deployments/enterprise-ha/docker-compose/base-services/.env delete mode 100644 openvidu-server/deployments/enterprise-ha/docker-compose/base-services/base-services delete mode 100644 openvidu-server/deployments/enterprise-ha/docker-compose/base-services/docker-compose.override.yml delete mode 100644 openvidu-server/deployments/enterprise-ha/docker-compose/base-services/docker-compose.yml delete mode 100644 openvidu-server/deployments/enterprise-ha/docker-compose/base-services/install_ov_enterprise_ha_base.sh delete mode 100644 openvidu-server/deployments/enterprise-ha/docker-compose/node/.env delete mode 100644 openvidu-server/deployments/enterprise-ha/docker-compose/node/beats/filebeat.yml delete mode 100644 openvidu-server/deployments/enterprise-ha/docker-compose/node/beats/metricbeat-elasticsearch.yml delete mode 100644 openvidu-server/deployments/enterprise-ha/docker-compose/node/docker-compose.yml delete mode 100755 openvidu-server/deployments/enterprise-ha/docker-compose/node/install_openvidu_enterprise_ha_node.sh delete mode 100755 openvidu-server/deployments/enterprise-ha/docker-compose/node/openvidu delete mode 100644 openvidu-server/deployments/enterprise/aws/CF-OpenVidu-Enterprise.yaml.template delete mode 100644 openvidu-server/deployments/enterprise/aws/cfn-crete-ov-aws-asg-ami.yaml.template delete mode 100755 openvidu-server/deployments/enterprise/aws/createAMI.sh delete mode 100644 openvidu-server/deployments/enterprise/aws/dev/CF-OpenVidu-Enterprise-dev-master.yaml delete mode 100644 openvidu-server/deployments/enterprise/aws/dev/README.md delete mode 100644 openvidu-server/deployments/enterprise/aws/dev/cfn-crete-ov-aws-asg-ami.yaml.template delete mode 100755 openvidu-server/deployments/enterprise/aws/dev/createAMI.sh delete mode 100755 openvidu-server/deployments/enterprise/aws/dev/deploy_cf.sh delete mode 100755 openvidu-server/deployments/enterprise/aws/dev/test_sqs_unique_messages.sh delete mode 100644 openvidu-server/deployments/enterprise/docker-compose/.env delete mode 100644 openvidu-server/deployments/enterprise/docker-compose/beats/filebeat.yml delete mode 100644 openvidu-server/deployments/enterprise/docker-compose/beats/metricbeat.yml delete mode 100644 openvidu-server/deployments/enterprise/docker-compose/cluster/aws/openvidu_autodiscover.sh delete mode 100644 openvidu-server/deployments/enterprise/docker-compose/cluster/aws/openvidu_drop.sh delete mode 100644 openvidu-server/deployments/enterprise/docker-compose/cluster/aws/openvidu_launch_kms.sh delete mode 100644 openvidu-server/deployments/enterprise/docker-compose/docker-compose.yml delete mode 100755 openvidu-server/deployments/enterprise/docker-compose/install_openvidu_enterprise_master_node.sh delete mode 100755 openvidu-server/deployments/enterprise/docker-compose/openvidu delete mode 100644 openvidu-server/deployments/external-turn/aws/CF-External-Turn.yml delete mode 100644 openvidu-server/deployments/external-turn/docker-compose/.env delete mode 100755 openvidu-server/deployments/external-turn/docker-compose/certbot.sh delete mode 100644 openvidu-server/deployments/external-turn/docker-compose/docker-compose.yml delete mode 100755 openvidu-server/deployments/external-turn/docker-compose/install_openvidu_external_coturn.sh delete mode 100644 openvidu-server/deployments/pro/aws/CF-OpenVidu-Pro.yaml.template delete mode 100644 openvidu-server/deployments/pro/aws/cfn-mkt-kms-ami.yaml.template delete mode 100644 openvidu-server/deployments/pro/aws/cfn-mkt-ov-ami.yaml.template delete mode 100755 openvidu-server/deployments/pro/aws/createAMI.sh delete mode 100755 openvidu-server/deployments/pro/aws/delete_amis.sh delete mode 100755 openvidu-server/deployments/pro/aws/get_ov_media_node_ami_id.sh delete mode 100644 openvidu-server/deployments/pro/docker-compose/media-node/beats/copy_config_files.sh delete mode 100644 openvidu-server/deployments/pro/docker-compose/media-node/beats/filebeat.yml delete mode 100644 openvidu-server/deployments/pro/docker-compose/media-node/beats/metricbeat-elasticsearch.yml delete mode 100644 openvidu-server/deployments/pro/docker-compose/media-node/docker-compose.yml delete mode 100755 openvidu-server/deployments/pro/docker-compose/media-node/install_media_node.sh delete mode 100755 openvidu-server/deployments/pro/docker-compose/media-node/media_node delete mode 100644 openvidu-server/deployments/pro/docker-compose/mono-node/.env delete mode 100644 openvidu-server/deployments/pro/docker-compose/mono-node/beats/filebeat.yml delete mode 100644 openvidu-server/deployments/pro/docker-compose/mono-node/beats/metricbeat-elasticsearch.yml delete mode 100644 openvidu-server/deployments/pro/docker-compose/mono-node/docker-compose.override.yml delete mode 100644 openvidu-server/deployments/pro/docker-compose/mono-node/docker-compose.yml delete mode 100755 openvidu-server/deployments/pro/docker-compose/mono-node/install_openvidu_pro_mono_node.sh delete mode 100755 openvidu-server/deployments/pro/docker-compose/mono-node/openvidu delete mode 100644 openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env delete mode 100644 openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/beats/filebeat.yml delete mode 100644 openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/beats/metricbeat.yml delete mode 100644 openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/cluster/aws/openvidu_autodiscover.sh delete mode 100644 openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/cluster/aws/openvidu_drop.sh delete mode 100644 openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/cluster/aws/openvidu_launch_kms.sh delete mode 100644 openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/docker-compose.override.yml delete mode 100644 openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/docker-compose.yml delete mode 100755 openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/install_openvidu_pro.sh delete mode 100755 openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/openvidu delete mode 100644 openvidu-server/docker/.gitignore delete mode 100644 openvidu-server/docker/openvidu-coturn/Dockerfile delete mode 100644 openvidu-server/docker/openvidu-coturn/README.md delete mode 100755 openvidu-server/docker/openvidu-coturn/create_image.sh delete mode 100644 openvidu-server/docker/openvidu-coturn/detect-external-ip.sh delete mode 100644 openvidu-server/docker/openvidu-coturn/discover-internal-ip.sh delete mode 100644 openvidu-server/docker/openvidu-coturn/docker-entrypoint.sh delete mode 100644 openvidu-server/docker/openvidu-deployment-tester/.dockerignore delete mode 100644 openvidu-server/docker/openvidu-deployment-tester/.gitignore delete mode 100644 openvidu-server/docker/openvidu-deployment-tester/Dockerfile delete mode 100644 openvidu-server/docker/openvidu-deployment-tester/README.md delete mode 100755 openvidu-server/docker/openvidu-deployment-tester/create_image.sh delete mode 100644 openvidu-server/docker/openvidu-deployment-tester/entrypoint.sh delete mode 100644 openvidu-server/docker/openvidu-deployment-tester/requirements.txt delete mode 100644 openvidu-server/docker/openvidu-deployment-tester/src/cli_utils.py delete mode 100644 openvidu-server/docker/openvidu-deployment-tester/src/main.py delete mode 100644 openvidu-server/docker/openvidu-deployment-tester/src/tests.py delete mode 100644 openvidu-server/docker/openvidu-deployment-tester/src/utils.py delete mode 100644 openvidu-server/docker/openvidu-dev/Dockerfile delete mode 100755 openvidu-server/docker/openvidu-dev/create_image.sh delete mode 100644 openvidu-server/docker/openvidu-dev/kms.sh delete mode 100644 openvidu-server/docker/openvidu-dev/supervisord.conf delete mode 100644 openvidu-server/docker/openvidu-elasticsearch/.gitignore delete mode 100755 openvidu-server/docker/openvidu-elasticsearch/create_image.sh delete mode 100644 openvidu-server/docker/openvidu-elasticsearch/elasticsearch_7.6.2_patch_log4j.diff delete mode 100644 openvidu-server/docker/openvidu-elasticsearch/elasticsearch_7.8.0_patch_log4j.diff delete mode 100644 openvidu-server/docker/openvidu-proxy/Dockerfile delete mode 100755 openvidu-server/docker/openvidu-proxy/create_image.sh delete mode 100644 openvidu-server/docker/openvidu-proxy/default.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/ce/default.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/enterprise-ha/default.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/app_config.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/app_config_default.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/app_upstream.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ce/common_api_ce.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ce/deprecated_api_ce.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ce/new_api_ce.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ce/redirect_www.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/enterprise-ha/app_upstream.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/enterprise-ha/common_api_enterprise.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/enterprise-ha/dashboard_inspector_rules_enterprise.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/nginx_status.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/common_api_pro.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/dashboard_inspector_rules_pro.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/deprecated_api_pro.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/new_api_pro.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/redirect_www.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/proxy_config.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/redirect_www_ssl.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ssl_config.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/xframe_sameorigin.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/default_nginx_conf/pro/default.conf delete mode 100755 openvidu-server/docker/openvidu-proxy/entrypoint.sh delete mode 100644 openvidu-server/docker/openvidu-proxy/nginx.conf delete mode 100644 openvidu-server/docker/openvidu-proxy/update_enterprise_ha_nodes.sh delete mode 100644 openvidu-server/docker/openvidu-recording/Dockerfile delete mode 100755 openvidu-server/docker/openvidu-recording/create_image.sh delete mode 100755 openvidu-server/docker/openvidu-recording/entrypoint.sh delete mode 100644 openvidu-server/docker/openvidu-recording/firefox/Dockerfile delete mode 100644 openvidu-server/docker/openvidu-recording/firefox/configuration/autoconfig.js delete mode 100644 openvidu-server/docker/openvidu-recording/firefox/configuration/customconfig.cfg delete mode 100644 openvidu-server/docker/openvidu-recording/firefox/configuration/forcefull@shdon.com.xpi delete mode 100644 openvidu-server/docker/openvidu-recording/firefox/configuration/{d320c473-63c2-47ab-87f8-693b1badb5e3}.xpi delete mode 100755 openvidu-server/docker/openvidu-recording/firefox/create_image.sh delete mode 100755 openvidu-server/docker/openvidu-recording/firefox/entrypoint.sh delete mode 100644 openvidu-server/docker/openvidu-recording/scripts/broadcast.sh delete mode 100644 openvidu-server/docker/openvidu-recording/scripts/composed.sh delete mode 100644 openvidu-server/docker/openvidu-recording/scripts/composed_quick_start.sh delete mode 100644 openvidu-server/docker/openvidu-recording/utils/headless-chrome.sh delete mode 100644 openvidu-server/docker/openvidu-recording/utils/xvfb-run-safe delete mode 100644 openvidu-server/docker/openvidu-server-pro/Dockerfile delete mode 100755 openvidu-server/docker/openvidu-server-pro/create_image.sh delete mode 100755 openvidu-server/docker/openvidu-server-pro/entrypoint.sh delete mode 100644 openvidu-server/docker/openvidu-server/Dockerfile delete mode 100755 openvidu-server/docker/openvidu-server/create_image.sh delete mode 100644 openvidu-server/docker/openvidu-server/entrypoint.sh delete mode 100644 openvidu-server/docker/utils/coturn-shared-key.template delete mode 100755 openvidu-server/docker/utils/discover_my_public_ip.sh delete mode 100644 openvidu-server/pom.xml delete mode 100644 openvidu-server/sonar-project.properties delete mode 100644 openvidu-server/src/dashboard/.editorconfig delete mode 100644 openvidu-server/src/dashboard/.gitignore delete mode 100644 openvidu-server/src/dashboard/README.md delete mode 100644 openvidu-server/src/dashboard/angular.json delete mode 100644 openvidu-server/src/dashboard/e2e/app.e2e-spec.ts delete mode 100644 openvidu-server/src/dashboard/e2e/app.po.ts delete mode 100644 openvidu-server/src/dashboard/e2e/tsconfig.e2e.json delete mode 100644 openvidu-server/src/dashboard/karma.conf.js delete mode 100644 openvidu-server/src/dashboard/package-lock.json delete mode 100644 openvidu-server/src/dashboard/package.json delete mode 100644 openvidu-server/src/dashboard/protractor.conf.js delete mode 100644 openvidu-server/src/dashboard/src/app/app.component.css delete mode 100644 openvidu-server/src/dashboard/src/app/app.component.html delete mode 100644 openvidu-server/src/dashboard/src/app/app.component.spec.ts delete mode 100644 openvidu-server/src/dashboard/src/app/app.component.ts delete mode 100644 openvidu-server/src/dashboard/src/app/app.material.module.ts delete mode 100644 openvidu-server/src/dashboard/src/app/app.module.ts delete mode 100644 openvidu-server/src/dashboard/src/app/app.routing.ts delete mode 100644 openvidu-server/src/dashboard/src/app/components/dashboard/credentials-dialog.component.ts delete mode 100644 openvidu-server/src/dashboard/src/app/components/dashboard/dashboard.component.css delete mode 100644 openvidu-server/src/dashboard/src/app/components/dashboard/dashboard.component.html delete mode 100644 openvidu-server/src/dashboard/src/app/components/dashboard/dashboard.component.spec.ts delete mode 100644 openvidu-server/src/dashboard/src/app/components/dashboard/dashboard.component.ts delete mode 100644 openvidu-server/src/dashboard/src/app/components/layouts/layout-base/layout-base.component.css delete mode 100644 openvidu-server/src/dashboard/src/app/components/layouts/layout-base/layout-base.component.html delete mode 100644 openvidu-server/src/dashboard/src/app/components/layouts/layout-base/layout-base.component.spec.ts delete mode 100644 openvidu-server/src/dashboard/src/app/components/layouts/layout-base/layout-base.component.ts delete mode 100644 openvidu-server/src/dashboard/src/app/components/layouts/layout-best-fit/layout-best-fit.component.ts delete mode 100644 openvidu-server/src/dashboard/src/app/components/layouts/layout-horizontal-presentation/layout-horizontal-presentation.component.ts delete mode 100644 openvidu-server/src/dashboard/src/app/components/layouts/layout-vertical-presentation/layout-vertical-presentation.component.ts delete mode 100644 openvidu-server/src/dashboard/src/app/components/layouts/openvidu-layout.ts delete mode 100644 openvidu-server/src/dashboard/src/app/components/layouts/ov-video.component.ts delete mode 100644 openvidu-server/src/dashboard/src/app/components/session-details/session-details.component.css delete mode 100644 openvidu-server/src/dashboard/src/app/components/session-details/session-details.component.html delete mode 100644 openvidu-server/src/dashboard/src/app/components/session-details/session-details.component.spec.ts delete mode 100644 openvidu-server/src/dashboard/src/app/components/session-details/session-details.component.ts delete mode 100644 openvidu-server/src/dashboard/src/app/services/info.service.spec.ts delete mode 100644 openvidu-server/src/dashboard/src/app/services/info.service.ts delete mode 100644 openvidu-server/src/dashboard/src/app/services/rest.service.ts delete mode 100644 openvidu-server/src/dashboard/src/assets/.gitkeep delete mode 100644 openvidu-server/src/dashboard/src/environments/environment.ts delete mode 100644 openvidu-server/src/dashboard/src/favicon.ico delete mode 100644 openvidu-server/src/dashboard/src/index.html delete mode 100644 openvidu-server/src/dashboard/src/main.ts delete mode 100644 openvidu-server/src/dashboard/src/polyfills.ts delete mode 100644 openvidu-server/src/dashboard/src/styles.css delete mode 100644 openvidu-server/src/dashboard/src/test.ts delete mode 100644 openvidu-server/src/dashboard/src/tsconfig.app.json delete mode 100644 openvidu-server/src/dashboard/src/tsconfig.spec.json delete mode 100644 openvidu-server/src/dashboard/src/typings.d.ts delete mode 100644 openvidu-server/src/dashboard/tsconfig.json delete mode 100644 openvidu-server/src/dashboard/tslint.json delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/OpenViduServer.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/broadcast/BroadcastManager.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/broadcast/BroadcastManagerDummy.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/cdr/CDREvent.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventEnd.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventFilterEvent.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventName.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventParticipant.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventRecordingStatusChanged.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventSession.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventSignal.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventWebrtcConnection.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/cdr/CDRLogger.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/cdr/CDRLoggerFile.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/cdr/CallDetailRecord.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/cdr/WebrtcDebugEvent.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/config/Dotenv.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/config/HttpHandshakeInterceptor.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/config/InfoHandler.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/config/InfoSocketConfig.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/config/OpenviduBuildInfo.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/config/SecurityConfig.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/core/EndReason.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/core/FinalUser.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/core/IdentifierPrefixes.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/core/MediaOptions.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/core/MediaServer.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/core/Participant.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/core/Session.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/core/SessionInterface.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/core/Token.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/core/TokenGenerator.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/core/TokenRegister.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/coturn/CoturnCredentialsService.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/coturn/TurnCredentials.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoMediaOptions.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipantEndpointConfig.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionEventsHandler.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/EndpointType.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/KmsEvent.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/KmsMediaEvent.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/KurentoFilter.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/PublisherEndpoint.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/SdpType.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/SubscriberEndpoint.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/TrackType.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/kms/DummyLoadManager.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/kms/FixedOneKmsManager.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/kms/Kms.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/kms/KmsManager.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/kms/KmsProperties.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/kms/LoadManager.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/CompositeWrapper.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/DummyRecordingDownloader.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/DummyRecordingUploader.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/RecorderEndpointWrapper.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/Recording.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/RecordingDownloader.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/RecordingInfoUtils.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/RecordingNotification.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/RecordingUploader.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManagerUtils.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManagerUtilsLocalStorage.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingService.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/service/WaitForContainerStoppedCallback.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/resources/CDRHttpHandler.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/resources/FrontendResourceHandler.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/resources/RecordingCustomLayoutsResourceHandler.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/resources/RecordingsResourceHandler.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/rest/ApiRestPathRewriteFilter.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/rest/CDRRestController.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/rest/CertificateRestController.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/rest/ConfigRestController.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/rest/RequestMappings.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/rpc/RpcConnection.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/rpc/RpcNotificationService.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/summary/ParticipantSummary.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/summary/SessionSummary.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/CommandExecutor.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/CustomFileManager.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/DockerManager.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/GeoLocation.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/GeoLocationByIp.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/GeoLocationByIpDummy.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/JsonUtils.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/LocalCustomFileManager.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/LocalDockerManager.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/MediaNodeManager.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/MediaNodeManagerDummy.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/RecordingUtils.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/RemoteOperationUtils.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/RestUtils.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/SDPMunging.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/UpdatableTimerTask.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/VersionComparator.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/ice/IceCandidateDataParser.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/utils/ice/IceCandidateType.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/webhook/CDRLoggerWebhook.java delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/webhook/HttpWebhookSender.java delete mode 100644 openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json delete mode 100644 openvidu-server/src/main/resources/OpenVidu.postman_collection.json delete mode 100644 openvidu-server/src/main/resources/application-container.properties delete mode 100644 openvidu-server/src/main/resources/application.properties delete mode 100644 openvidu-server/src/main/resources/banner.txt delete mode 100644 openvidu-server/src/main/resources/logback-spring.xml delete mode 100644 openvidu-server/src/main/resources/openvidu-selfsigned.p12 delete mode 100644 openvidu-server/src/main/resources/static/index.html delete mode 100644 openvidu-server/src/main/resources/templates/accept-cert.html delete mode 100644 openvidu-server/src/main/resources/templates/error/404.html delete mode 100644 openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java delete mode 100644 openvidu-server/src/test/java/io/openvidu/server/test/integration/WebhookIntegrationTest.java delete mode 100644 openvidu-server/src/test/java/io/openvidu/server/test/integration/config/IntegrationTestConfiguration.java delete mode 100644 openvidu-server/src/test/java/io/openvidu/server/test/unit/DotenvTest.java delete mode 100644 openvidu-server/src/test/java/io/openvidu/server/test/unit/IceServerPropertiesTest.java delete mode 100644 openvidu-server/src/test/java/io/openvidu/server/test/unit/SDPMungingTest.java delete mode 100644 openvidu-server/src/test/java/io/openvidu/server/test/unit/VersionComparatorTest.java delete mode 100644 openvidu-server/src/test/resources/.env delete mode 100644 openvidu-server/src/test/resources/application.properties delete mode 100644 openvidu-server/src/test/resources/integration-test.properties delete mode 100644 openvidu-server/src/test/resources/log4j.properties delete mode 100644 openvidu-server/src/test/resources/sdp/sdp_chrome84.txt delete mode 100644 openvidu-server/src/test/resources/sdp/sdp_firefox79.txt delete mode 100644 openvidu-server/src/test/resources/sdp/sdp_kurento_h264.txt delete mode 100644 openvidu-server/src/test/resources/sdp/sdp_safari13-1.txt delete mode 100644 openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduMobileE2eTest.java delete mode 100644 openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduProTestAppE2eTest.java delete mode 100644 openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/annotations/OnlyKurento.java create mode 100644 openvidu-testapp/.vscode/extensions.json create mode 100644 openvidu-testapp/.vscode/launch.json create mode 100644 openvidu-testapp/.vscode/tasks.json delete mode 100644 openvidu-testapp/cert.pem delete mode 100644 openvidu-testapp/karma.conf.js delete mode 100644 openvidu-testapp/key.pem create mode 100644 openvidu-testapp/src/app/app-routing.module.ts delete mode 100644 openvidu-testapp/src/app/app.material.module.ts delete mode 100644 openvidu-testapp/src/app/app.routing.ts create mode 100644 openvidu-testapp/src/app/components/audio-track/audio-track.component.css create mode 100644 openvidu-testapp/src/app/components/audio-track/audio-track.component.html create mode 100644 openvidu-testapp/src/app/components/audio-track/audio-track.component.spec.ts create mode 100644 openvidu-testapp/src/app/components/audio-track/audio-track.component.ts delete mode 100644 openvidu-testapp/src/app/components/dialogs/extension-dialog/extension-dialog.component.ts delete mode 100644 openvidu-testapp/src/app/components/dialogs/local-recording-dialog/local-recording-dialog.component.ts delete mode 100644 openvidu-testapp/src/app/components/dialogs/other-stream-operations-dialog/other-stream-operations-dialog.component.css delete mode 100644 openvidu-testapp/src/app/components/dialogs/other-stream-operations-dialog/other-stream-operations-dialog.component.html delete mode 100644 openvidu-testapp/src/app/components/dialogs/other-stream-operations-dialog/other-stream-operations-dialog.component.ts delete mode 100644 openvidu-testapp/src/app/components/dialogs/publisher-properties-dialog/publisher-properties-dialog.component.css delete mode 100644 openvidu-testapp/src/app/components/dialogs/publisher-properties-dialog/publisher-properties-dialog.component.html delete mode 100644 openvidu-testapp/src/app/components/dialogs/publisher-properties-dialog/publisher-properties-dialog.component.ts delete mode 100644 openvidu-testapp/src/app/components/dialogs/recording-properties/recording-properties.component.css delete mode 100644 openvidu-testapp/src/app/components/dialogs/recording-properties/recording-properties.component.html delete mode 100644 openvidu-testapp/src/app/components/dialogs/recording-properties/recording-properties.component.ts create mode 100644 openvidu-testapp/src/app/components/dialogs/room-api-dialog/room-api-dialog.component.css create mode 100644 openvidu-testapp/src/app/components/dialogs/room-api-dialog/room-api-dialog.component.html create mode 100644 openvidu-testapp/src/app/components/dialogs/room-api-dialog/room-api-dialog.component.ts rename openvidu-server/deployments/ce/docker-compose/owncert/.gitignore => openvidu-testapp/src/app/components/dialogs/room-options-dialog/room-options-dialog.component.css (100%) create mode 100644 openvidu-testapp/src/app/components/dialogs/room-options-dialog/room-options-dialog.component.html create mode 100644 openvidu-testapp/src/app/components/dialogs/room-options-dialog/room-options-dialog.component.spec.ts create mode 100644 openvidu-testapp/src/app/components/dialogs/room-options-dialog/room-options-dialog.component.ts delete mode 100644 openvidu-testapp/src/app/components/dialogs/scenario-properties-dialog/scenario-properties-dialog.component.css delete mode 100644 openvidu-testapp/src/app/components/dialogs/scenario-properties-dialog/scenario-properties-dialog.component.html delete mode 100644 openvidu-testapp/src/app/components/dialogs/scenario-properties-dialog/scenario-properties-dialog.component.ts delete mode 100644 openvidu-testapp/src/app/components/dialogs/session-api-dialog/session-api-dialog.component.css delete mode 100644 openvidu-testapp/src/app/components/dialogs/session-api-dialog/session-api-dialog.component.html delete mode 100644 openvidu-testapp/src/app/components/dialogs/session-api-dialog/session-api-dialog.component.ts delete mode 100644 openvidu-testapp/src/app/components/dialogs/session-info-dialog/session-info-dialog.component.ts delete mode 100644 openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.css delete mode 100644 openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.html delete mode 100644 openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.ts delete mode 100644 openvidu-testapp/src/app/components/dialogs/show-codec-dialog/show-codec-dialog.component.ts delete mode 100644 openvidu-testapp/src/app/components/dialogs/show-configured-ice/show-configured-ice.component.ts create mode 100644 openvidu-testapp/src/app/components/participant/participant.component.css create mode 100644 openvidu-testapp/src/app/components/participant/participant.component.html create mode 100644 openvidu-testapp/src/app/components/participant/participant.component.spec.ts create mode 100644 openvidu-testapp/src/app/components/participant/participant.component.ts delete mode 100644 openvidu-testapp/src/app/components/test-apirest/test-apirest.component.css delete mode 100644 openvidu-testapp/src/app/components/test-apirest/test-apirest.component.html delete mode 100644 openvidu-testapp/src/app/components/test-apirest/test-apirest.component.spec.ts delete mode 100644 openvidu-testapp/src/app/components/test-apirest/test-apirest.component.ts delete mode 100644 openvidu-testapp/src/app/components/test-scenarios/test-scenarios.component.css delete mode 100644 openvidu-testapp/src/app/components/test-scenarios/test-scenarios.component.html delete mode 100644 openvidu-testapp/src/app/components/test-scenarios/test-scenarios.component.spec.ts delete mode 100644 openvidu-testapp/src/app/components/test-scenarios/test-scenarios.component.ts create mode 100644 openvidu-testapp/src/app/components/track/track.component.ts delete mode 100644 openvidu-testapp/src/app/components/users-table/table-video.component.ts delete mode 100644 openvidu-testapp/src/app/components/users-table/users-table.component.css delete mode 100644 openvidu-testapp/src/app/components/users-table/users-table.component.html delete mode 100644 openvidu-testapp/src/app/components/users-table/users-table.component.ts create mode 100644 openvidu-testapp/src/app/components/video-track/video-track.component.css create mode 100644 openvidu-testapp/src/app/components/video-track/video-track.component.html create mode 100644 openvidu-testapp/src/app/components/video-track/video-track.component.spec.ts create mode 100644 openvidu-testapp/src/app/components/video-track/video-track.component.ts delete mode 100644 openvidu-testapp/src/app/components/video/ov-video.component.ts delete mode 100644 openvidu-testapp/src/app/components/video/video.component.css delete mode 100644 openvidu-testapp/src/app/components/video/video.component.html delete mode 100644 openvidu-testapp/src/app/components/video/video.component.ts create mode 100644 openvidu-testapp/src/app/services/livekit-params.service.spec.ts create mode 100644 openvidu-testapp/src/app/services/livekit-params.service.ts delete mode 100644 openvidu-testapp/src/app/services/mute-subscribers.service.ts delete mode 100644 openvidu-testapp/src/app/services/openvidu-params.service.spec.ts delete mode 100644 openvidu-testapp/src/app/services/openvidu-params.service.ts delete mode 100644 openvidu-testapp/src/app/services/openvidu-rest.service.spec.ts delete mode 100644 openvidu-testapp/src/app/services/openvidu-rest.service.ts create mode 100644 openvidu-testapp/src/app/services/room-api.service.ts delete mode 100644 openvidu-testapp/src/app/services/test-feed.service.spec.ts delete mode 100644 openvidu-testapp/src/assets/.npmignore delete mode 100644 openvidu-testapp/src/assets/fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2 delete mode 100644 openvidu-testapp/src/assets/images/openvidu_globe_bg_transp_cropped.png delete mode 100644 openvidu-testapp/src/assets/images/openvidu_grey_bg_transp_cropped.png delete mode 100644 openvidu-testapp/src/assets/images/openvidu_vert_white_bg_trans.png delete mode 100644 openvidu-testapp/src/assets/images/volume.png delete mode 100644 openvidu-testapp/src/environments/environment.prod.ts delete mode 100644 openvidu-testapp/src/environments/environment.ts delete mode 100644 openvidu-testapp/src/material-icons.css delete mode 100644 openvidu-testapp/src/test.ts delete mode 100644 openvidu-testapp/src/tsconfig.app.json delete mode 100644 openvidu-testapp/src/tsconfig.spec.json delete mode 100644 openvidu-testapp/src/typings.d.ts create mode 100644 openvidu-testapp/tsconfig.app.json create mode 100644 openvidu-testapp/tsconfig.spec.json delete mode 100644 openvidu-testapp/tslint.json diff --git a/ci-scripts/commons/build.sh b/ci-scripts/commons/build.sh deleted file mode 100644 index 85ba6660..00000000 --- a/ci-scripts/commons/build.sh +++ /dev/null @@ -1,248 +0,0 @@ -#!/bin/bash -x -set -eu -o pipefail - -############################################################################ -# Any function offered by this file that is not path agnostic assumes that # -# the path is located where the first command of each function requires it # -############################################################################ - -CLEAN_ENVIRONMENT=false -BUILD_OV_BROWSER=false -BUILD_OV_NODE_CLIENT=false -BUILD_OV_JAVA_CLIENT=false -BUILD_OV_PARENT=false -BUILD_OV_TESTAPP=false -BUILD_OV_SERVER_DASHBOARD=false -BUILD_OV_SERVER=false -BUILD_OV_SERVER_DEPENDENCY=false -BUILD_OV_SERVER_PRO_INSPECTOR=false -BUILD_OV_SERVER_PRO=false -CHECK_AND_PREPARE_KURENTO_SNAPSHOT=false - -if [[ -n ${1:-} ]]; then - case "${1:-}" in - - --clean-environment) - CLEAN_ENVIRONMENT=true - ;; - - --build-openvidu-browser) - BUILD_OV_BROWSER=true - ;; - - --build-openvidu-node-client) - BUILD_OV_NODE_CLIENT=true - ;; - - --build-openvidu-java-client) - BUILD_OV_JAVA_CLIENT=true - ;; - - --build-openvidu-parent) - BUILD_OV_PARENT=true - ;; - - --build-openvidu-testapp) - BUILD_OV_TESTAPP=true - ;; - - --build-openvidu-server-dashboard) - if [[ -z "${2:-}" ]]; then - echo "Must provide LINK_LOCAL_DEPENDENCIES as 1st parameter" - exit 1 - fi - BUILD_OV_SERVER_DASHBOARD=true - LINK_LOCAL_DEPENDENCIES="${2}" - ;; - - --build-openvidu-server) - BUILD_OV_SERVER=true - ;; - - --build-openvidu-server-dependency) - BUILD_OV_SERVER_DEPENDENCY=true - ;; - - --build-openvidu-server-pro-inspector) - if [[ -z "${2:-}" ]]; then - echo "Must provide LINK_LOCAL_DEPENDENCIES as 1st parameter" - exit 1 - fi - BUILD_OV_SERVER_PRO_INSPECTOR=true - LINK_LOCAL_DEPENDENCIES="${2}" - ;; - - --build-openvidu-server-pro) - BUILD_OV_SERVER_PRO=true - ;; - --check-and-prepare-kurento-snapshot) - CHECK_AND_PREPARE_KURENTO_SNAPSHOT=true - ;; - - *) - echo "Unrecognized method $1" - exit 1 - ;; - - esac -else - echo "Must provide a method to execute as first parameter when calling the script" - exit 1 -fi - -# ------------- -# Clean environment -# ------------- -if [[ "${CLEAN_ENVIRONMENT}" == true ]]; then - - # Remove all running containers except test container and runner container - ids=$(docker ps -a -q) - for id in $ids; do - DOCKER_IMAGE=$(docker inspect --format='{{.Config.Image}}' $id) - if [[ "${DOCKER_IMAGE}" != *"$TEST_IMAGE"* ]] && - [[ "${DOCKER_IMAGE}" != *"runner-image"* ]]; then - echo "Removing container image '$DOCKER_IMAGE' with id '$id'" - docker stop $id && docker rm $id - fi - done - - # Clean /opt/openvidu contents - rm -rf /opt/openvidu/* - -fi - -# ------------- -# Build openvidu-browser -# ------------- -if [[ "${BUILD_OV_BROWSER}" == true ]]; then - pushd openvidu-browser || exit 1 - npm install - npm run build - npm link - npm pack - mv openvidu-browser-*.tgz /opt/openvidu - npm run browserify - npm run browserify-prod - popd -fi - -# ------------- -# Build openvidu-node-client -# ------------- -if [[ "${BUILD_OV_NODE_CLIENT}" == true ]]; then - pushd openvidu-node-client - npm install - npm run build - npm link - npm pack - mv openvidu-node-client-*.tgz /opt/openvidu - popd -fi - -# ------------- -# Build openvidu-java-client -# ------------- -if [[ "${BUILD_OV_JAVA_CLIENT}" == true ]]; then - pushd openvidu-java-client - MVN_VERSION=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec) - mvn -B clean compile package - mvn -B install:install-file -Dfile=target/openvidu-java-client-${MVN_VERSION}.jar \ - -DgroupId=io.openvidu \ - -DartifactId=openvidu-java-client \ - -Dversion=${MVN_VERSION} -Dpackaging=jar - popd -fi - -# ------------- -# Build openvidu-parent -# ------------- -if [[ "${BUILD_OV_PARENT}" == true ]]; then - mvn -B -DskipTests=true -Dmaven.artifact.threads=1 clean install -fi - -# ------------- -# Build openvidu-testapp -# ------------- -if [[ "${BUILD_OV_TESTAPP}" == true ]]; then - pushd openvidu-testapp - npm install - npm link openvidu-browser openvidu-node-client - export NG_CLI_ANALYTICS="false" && ./node_modules/@angular/cli/bin/ng.js build --configuration production --output-path=/opt/openvidu/testapp - popd -fi - -# ------------- -# Build openvidu-server dashboard -# ------------- -if [[ "${BUILD_OV_SERVER_DASHBOARD}" == true ]]; then - pushd openvidu-server/src/dashboard - npm install - if [[ "${LINK_LOCAL_DEPENDENCIES}" == true ]]; then - npm link openvidu-browser openvidu-node-client - fi - npm run build-prod - popd -fi - -# ------------- -# Build openvidu-server -# ------------- -if [[ "${BUILD_OV_SERVER}" == true ]]; then - pushd openvidu-server - mvn -B -DskipTests=true clean package - mv target/openvidu-server-*.jar /opt/openvidu - popd -fi - -# ------------- -# Build openvidu-server dependency -# ------------- -if [[ "${BUILD_OV_SERVER_DEPENDENCY}" == true ]]; then - pushd openvidu-server - mvn -B -DskipTests=true -Pdependency clean install - popd -fi - -# ------------- -# Build Inspector -# ------------- -if [[ "${BUILD_OV_SERVER_PRO_INSPECTOR}" == true ]]; then - pushd dashboard - npm install - if [[ "${LINK_LOCAL_DEPENDENCIES}" == true ]]; then - npm link openvidu-browser openvidu-node-client - fi - npm run build-server-prod - popd -fi - -# ------------- -# Build openvidu-server-pro -# ------------- -if [[ "${BUILD_OV_SERVER_PRO}" == true ]]; then - pushd openvidu-server-pro - mvn -B -DskipTests=true clean package - mv target/openvidu-server-pro-*.jar /opt/openvidu - popd -fi - -# ------------- -# Check kurento version from pom.xml -# If kurento version is a snapshot, configure snapshot builds -# ------------- -if [[ "${CHECK_AND_PREPARE_KURENTO_SNAPSHOT}" == true ]]; then - # Check if kurento version is a snapshot - KURENTO_VERSION=$(awk -F'[<>]' '// {print $3}' pom.xml) - if [[ "${KURENTO_VERSION}" == *"-SNAPSHOT" ]] && [[ -n "${KURENTO_SNAPSHOTS_URL:-}" ]]; then - echo "Kurento version is a SNAPSHOT: ${KURENTO_VERSION}" - mkdir -p /etc/maven - chmod -R 777 /etc/maven - pushd /etc/maven - rm -f settings.xml - curl https://raw.githubusercontent.com/OpenVidu/openvidu/master/ci-scripts/kurento-snapshots.xml -o settings.xml - sed -i "s|KURENTO_SNAPSHOTS_URL|${KURENTO_SNAPSHOTS_URL}|g" settings.xml - popd - else - echo "Kurento version is not a SNAPSHOT: ${KURENTO_VERSION}" - fi -fi diff --git a/ci-scripts/commons/bump.sh b/ci-scripts/commons/bump.sh deleted file mode 100644 index 63651266..00000000 --- a/ci-scripts/commons/bump.sh +++ /dev/null @@ -1,307 +0,0 @@ -#!/bin/bash -x -set -eu -o pipefail - -############################################################################ -# Any function offered by this file that is not path agnostic assumes that # -# the path is located where the first command of each function requires it # -############################################################################ - -# Bump versions -BUMP_NPM_PROJECT_VERSION=false -BUMP_NPM_DEPENDENCY_VERSION=false -BUMP_MAVEN_PROJECT_VERSION=false -BUMP_MAVEN_PROPERTY_VERSION=false -BUMP_MAVEN_DEPENDENCY_VERSION=false -BUMP_DOCKER_COMPOSE_SERVICE_VERSION=false -BUMP_DOCKER_COMPOSE_HEADER_VERSION=false -BUMP_DOCKER_IMAGE_VERSION_IN_FILES=false -BUMP_APPLICATION_PROPERTIES_VAR_VALUE=false -WAIT_FOR_NPM_DEPENDENCY=false -GENERIC_SED=false - -if [[ -n ${1:-} ]]; then - case "${1:-}" in - - --bump-npm-project-version) - if [[ -z "${2:-}" ]]; then - echo "Must provide VERSION as 1st parameter" - exit 1 - fi - BUMP_NPM_PROJECT_VERSION=true - VERSION="${2}" - ;; - - --bump-npm-dependency-version) - if [[ -z "${2:-}" ]]; then - echo "Must provide DEPENDENCY as 1st parameter" - exit 1 - fi - if [[ -z "${3:-}" ]]; then - echo "Must provide VERSION as 2nd parameter" - exit 1 - fi - BUMP_NPM_DEPENDENCY_VERSION=true - DEPENDENCY="${2}" - VERSION="${3}" - TYPE_OF_DEPENDENCY="${4:-dependencies}" # [dependencies, devDependencies, peerDependencies, optionalDependencies] - ;; - - --bump-maven-project-version) - if [[ -z "${2:-}" ]]; then - echo "Must provide VERSION as 1st parameter" - exit 1 - fi - BUMP_MAVEN_PROJECT_VERSION=true - VERSION="${2}" - ;; - - --bump-maven-property-version) - if [[ -z "${2:-}" ]]; then - echo "Must provide PROPERTY as 1st parameter" - exit 1 - fi - if [[ -z "${3:-}" ]]; then - echo "Must provide VERSION as 2nd parameter" - exit 1 - fi - BUMP_MAVEN_PROPERTY_VERSION=true - PROPERTY="${2}" - VERSION="${3}" - ;; - - --bump-maven-dependency-version) - if [[ -z "${2:-}" ]]; then - echo "Must provide DEPENDENCY as 1st parameter" - exit 1 - fi - if [[ -z "${3:-}" ]]; then - echo "Must provide VERSION as 2nd parameter" - exit 1 - fi - BUMP_MAVEN_DEPENDENCY_VERSION=true - DEPENDENCY="${2}" - VERSION="${3}" - ;; - - --bump-docker-compose-service-version) - if [[ -z "${2:-}" ]]; then - echo "Must provide DOCKER_COMPOSE_FILE as 1st parameter" - exit 1 - fi - if [[ -z "${3:-}" ]]; then - echo "Must provide SERVICE_IMAGE as 2nd parameter" - exit 1 - fi - if [[ -z "${4:-}" ]]; then - echo "Must provide VERSION as 3rd parameter" - exit 1 - fi - BUMP_DOCKER_COMPOSE_SERVICE_VERSION=true - DOCKER_COMPOSE_FILE="${2}" - SERVICE_IMAGE="${3}" - VERSION="${4}" - ;; - - --bump-docker-compose-header-version) - if [[ -z "${2:-}" ]]; then - echo "Must provide DOCKER_COMPOSE_FILE as 1st parameter" - exit 1 - fi - if [[ -z "${3:-}" ]]; then - echo "Must provide HEADER as 2nd parameter" - exit 1 - fi - if [[ -z "${4:-}" ]]; then - echo "Must provide VERSION as 3rd parameter" - exit 1 - fi - BUMP_DOCKER_COMPOSE_HEADER_VERSION=true - DOCKER_COMPOSE_FILE="${2}" - HEADER="${3}" - VERSION="${4}" - ;; - - --bump-docker-image-version-in-files) - if [[ -z "${3:-}" ]]; then - echo "Must provide FILE_NAME_PATTERN as 1st parameter" - exit 1 - fi - if [[ -z "${4:-}" ]]; then - echo "Must provide IMAGE as 2nd parameter" - exit 1 - fi - if [[ -z "${4:-}" ]]; then - echo "Must provide VERSION as 3rd parameter" - exit 1 - fi - BUMP_DOCKER_IMAGE_VERSION_IN_FILES=true - FILE_NAME_PATTERN="${2}" - IMAGE="${3}" - VERSION="${4}" - ;; - - --bump-application-properties-var-value) - if [[ -z "${3:-}" ]]; then - echo "Must provide APPLICATION_PROPERTIES_FILE as 2nd parameter" - exit 1 - fi - if [[ -z "${4:-}" ]]; then - echo "Must provide VARIABLE as 3rd parameter" - exit 1 - fi - if [[ -z "${4:-}" ]]; then - echo "Must provide VALUE as 4th parameter" - exit 1 - fi - BUMP_APPLICATION_PROPERTIES_VAR_VALUE=true - APPLICATION_PROPERTIES_FILE="${2}" - VARIABLE="${3}" - VALUE="${4}" - ;; - - --wait-for-npm-dependency) - if [[ -z "${2:-}" ]]; then - echo "Must provide DEPENDENCY as 1st parameter" - exit 1 - fi - if [[ -z "${3:-}" ]]; then - echo "Must provide VERSION as 2nd parameter" - exit 1 - fi - WAIT_FOR_NPM_DEPENDENCY=true - DEPENDENCY="${2}" - VERSION="${3}" - ;; - - --generic-sed) - if [[ -z "${2:-}" ]]; then - echo "Must provide FILE as 1st parameter" - exit 1 - fi - if [[ -z "${3:-}" ]]; then - echo "Must provide SED_EXPRESSION as 2nd parameter" - exit 1 - fi - GENERIC_SED=true - FILE="${2}" - SED_EXPRESSION="${3}" - ;; - - *) - echo "Unrecognized method $1" - exit 1 - ;; - - esac -else - echo "Must provide a method to execute as first parameter when calling the script" - exit 1 -fi - -compareFiles() { - if cmp -s "$1" "$1-AUX"; then - rm -f $1-AUX - echo "Error: no changes has been made to $1" - echo "Trying to change \"$2\" to \"$3\"" - exit 1 - else - cp -f "$1-AUX" "$1" - rm -f "$1-AUX" - fi -} - -# ------------- -# Bump NPM project version -# ------------- -if [[ "${BUMP_NPM_PROJECT_VERSION}" == true ]]; then - npm version ${VERSION} --git-tag-version=false --commit-hooks=false -fi - -# ------------- -# Bump NPM project dependency -# ------------- -if [[ "${BUMP_NPM_DEPENDENCY_VERSION}" == true ]]; then - jq -j ".${TYPE_OF_DEPENDENCY}.\"${DEPENDENCY}\" = \"${VERSION}\"" package.json >package.json-AUX - compareFiles package.json version $VERSION -fi - -# ------------- -# Bump Maven project version -# ------------- -if [[ "${BUMP_MAVEN_PROJECT_VERSION}" == true ]]; then - cp pom.xml pom.xml-AUX - mvn -DskipTests=true versions:set -DnewVersion="${VERSION}" - mv pom.xml changed-pom.xml && mv pom.xml-AUX pom.xml && mv changed-pom.xml pom.xml-AUX - compareFiles pom.xml "" $VERSION -fi - -# ------------- -# Bump Maven project property -# ------------- -if [[ "${BUMP_MAVEN_PROPERTY_VERSION}" == true ]]; then - mvn --batch-mode \ - -DskipTests=true \ - versions:set-property \ - -Dproperty="${PROPERTY}" \ - -DnewVersion="${VERSION}" -fi - -# ------------- -# Bump Maven dependency property -# ------------- -if [[ "${BUMP_MAVEN_DEPENDENCY_VERSION}" == true ]]; then - mvn --batch-mode versions:use-dep-version -Dincludes=$DEPENDENCY -DdepVersion=$VERSION -DforceVersion=true -fi - -# ------------- -# Bump docker-compose.yml service version -# ------------- -if [[ "${BUMP_DOCKER_COMPOSE_SERVICE_VERSION}" == true ]]; then - sed -r "s|image:\s+${SERVICE_IMAGE}:[[:alnum:]._-]+|image: ${SERVICE_IMAGE}:${VERSION}|g" ${DOCKER_COMPOSE_FILE} >${DOCKER_COMPOSE_FILE}-AUX - compareFiles $DOCKER_COMPOSE_FILE $SERVICE_IMAGE $VERSION -fi - -# ------------- -# Bump docker-compose.yml header version -# ------------- -if [[ "${BUMP_DOCKER_COMPOSE_HEADER_VERSION}" == true ]]; then - sed -r "s|#\s+${HEADER}:\s+[[:alnum:]._-]+|# ${HEADER}: ${VERSION}|g" ${DOCKER_COMPOSE_FILE} >${DOCKER_COMPOSE_FILE}-AUX - compareFiles $DOCKER_COMPOSE_FILE $HEADER $VERSION -fi - -# ------------- -# Bump Docker image version in files -# ------------- -if [[ "${BUMP_DOCKER_IMAGE_VERSION_IN_FILES}" == true ]]; then - find . -type f -name ${FILE_NAME_PATTERN} | xargs sed -i -r "s|${IMAGE}:[[:alnum:]._-]+|${IMAGE}:${VERSION}|g" -fi - -# ------------- -# Bump application.properties variable value -# ------------- -if [[ "${BUMP_APPLICATION_PROPERTIES_VAR_VALUE}" == true ]]; then - sed -r "s%${VARIABLE}((:|=)\s*).*$%${VARIABLE}\1${VALUE}%g" ${APPLICATION_PROPERTIES_FILE} >${APPLICATION_PROPERTIES_FILE}-AUX - compareFiles $APPLICATION_PROPERTIES_FILE $VARIABLE $VALUE -fi - -# ------------- -# Wait for NPM dependency to be available -# ------------- -if [[ "${WAIT_FOR_NPM_DEPENDENCY}" == true ]]; then - CHECK_VERSION_AVAILABILTY="npm show ${DEPENDENCY}@${VERSION} version || echo ''" - VERSION_AUX=$(eval "${CHECK_VERSION_AVAILABILTY}") - until [[ "${VERSION_AUX}" == "${VERSION}" ]]; do - echo "Waiting for ${DEPENDENCY}@${VERSION} to be available in NPM..." - sleep 2 - VERSION_AUX=$(eval "${CHECK_VERSION_AVAILABILTY}") - done - echo "${DEPENDENCY}@${VERSION} already available in NPM" -fi - -# ------------- -# Generic sed replacement -# ------------- -if [[ "${GENERIC_SED}" == true ]]; then - sed -r "$SED_EXPRESSION" ${FILE} >${FILE}-AUX - compareFiles $FILE "(generic sed)" "$SED_EXPRESSION" -fi diff --git a/ci-scripts/commons/test-utils.sh b/ci-scripts/commons/test-utils.sh deleted file mode 100644 index bd122077..00000000 --- a/ci-scripts/commons/test-utils.sh +++ /dev/null @@ -1,161 +0,0 @@ -#!/bin/bash -x -set -eu -o pipefail - -############################################################################ -# Any function offered by this file that is not path agnostic assumes that # -# the path is located where the first command of each function requires it # -############################################################################ - -# CI flags -PREPARE_TEST_ENVIRONMENT=false -USE_SPECIFIC_KURENTO_JAVA_COMMIT=false -SERVE_OV_TESTAPP=false -CHECK_AND_PREPARE_KURENTO_SNAPSHOT=false - -if [[ -n ${1:-} ]]; then - case "${1:-}" in - - --prepare-test-environment) - PREPARE_TEST_ENVIRONMENT=true - ;; - - --check-and-prepare-kurento-snapshot) - CHECK_AND_PREPARE_KURENTO_SNAPSHOT=true - ;; - - --use-specific-kurento-java-commit) - USE_SPECIFIC_KURENTO_JAVA_COMMIT=true - ;; - - --serve-openvidu-testapp) - SERVE_OV_TESTAPP=true - ;; - - *) - echo "Unrecognized method $1" - exit 1 - ;; - - esac -else - echo "Must provide a method to execute as first parameter when calling the script" - exit 1 -fi - -# ------------- -# Prepare build -# ------------- -if [[ "${PREPARE_TEST_ENVIRONMENT}" == true ]]; then - - # Connect e2e test container to network bridge so it is vissible for browser and media server containers - if [[ -n "${TEST_IMAGE}" ]]; then - E2E_CONTAINER_ID="$(docker ps | grep "$TEST_IMAGE" | awk '{ print $1 }')" || echo "Docker container not found for image ${TEST_IMAGE}" - if [[ -n "${E2E_CONTAINER_ID}" ]]; then - docker network connect bridge "${E2E_CONTAINER_ID}" - else - echo "Could not connect test docker container to docker bridge, because no running container was found for image \"${TEST_IMAGE}\"" - fi - else - echo "No TEST_IMAGE env var provided. Skipping network bridge connection" - fi - - # Prepare directory for OpenVidu recordings - sudo mkdir -p /opt/openvidu/recordings && sudo chmod 777 /opt/openvidu/recordings - # Prepare directory for OpenVidu Android apps - sudo mkdir -p /opt/openvidu/android && sudo chmod 777 /opt/openvidu/android - - # Download fake videos - FAKE_VIDEO1=/opt/openvidu/barcode.y4m - FAKE_VIDEO2=/opt/openvidu/girl.mjpeg - if [ ! -f ${FAKE_VIDEO1} ]; then - sudo curl --location https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/barcode.y4m --create-dirs --output /opt/openvidu/barcode.y4m - else - echo "File ${FAKE_VIDEO1} already exists" - fi - if [ ! -f ${FAKE_VIDEO2} ]; then - sudo curl --location https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/girl.mjpeg --create-dirs --output /opt/openvidu/girl.mjpeg - else - echo "File ${FAKE_VIDEO2} already exists" - fi - - # Download fake audios - FAKE_AUDIO1=/opt/openvidu/fakeaudio.wav - FAKE_AUDIO2=/opt/openvidu/stt-test.wav - if [ ! -f ${FAKE_AUDIO1} ]; then - sudo curl --location https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/fakeaudio.wav --create-dirs --output /opt/openvidu/fakeaudio.wav - else - echo "File ${FAKE_AUDIO1} already exists" - fi - if [ ! -f ${FAKE_AUDIO2} ]; then - sudo curl --location https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/stt-test.wav --create-dirs --output /opt/openvidu/stt-test.wav - else - echo "File ${FAKE_AUDIO2} already exists" - fi - - # Download recording custom layout - sudo curl --location https://raw.githubusercontent.com/OpenVidu/openvidu/master/openvidu-test-e2e/docker/my-custom-layout/index.html --create-dirs --output /opt/openvidu/test-layouts/layout1/index.html - - # Open permissions for /opt/openvidu folder - sudo chmod -R 777 /opt/openvidu - - # Pull browser images - # Pull chrome image if env variable CHROME_VERSION is set - if [[ -n "${CHROME_VERSION:-}" ]]; then - docker pull selenium/standalone-chrome:"${CHROME_VERSION}" - fi - # Pull firefox image if env variable FIREFOX_VERSION is set - if [[ -n "${FIREFOX_VERSION:-}" ]]; then - docker pull selenium/standalone-firefox:"${FIREFOX_VERSION}" - fi - # Pull edge image if env variable EDGE_VERSION is set - if [[ -n "${EDGE_VERSION:-}" ]]; then - docker pull selenium/standalone-edge:"${EDGE_VERSION}" - fi - # Pull Docker Android image if env variable DOCKER_ANDROID_IMAGE is set - if [[ -n "${DOCKER_ANDROID_IMAGE:-}" ]]; then - docker pull "${DOCKER_ANDROID_IMAGE}" - fi - - # Pull mediasoup and kurento - if [[ -n "${MEDIASOUP_CONTROLLER_VERSION:-}" ]]; then - docker pull openvidu/mediasoup-controller:"${MEDIASOUP_CONTROLLER_VERSION}" - fi - if [[ -n "${KURENTO_MEDIA_SERVER_IMAGE:-}" ]]; then - docker pull "${KURENTO_MEDIA_SERVER_IMAGE}" - fi - -fi - -# ------------- -# Use a specific kurento-java commit other than the configured in openvidu-parent pom.xml -# ------------- -if [[ "${USE_SPECIFIC_KURENTO_JAVA_COMMIT}" == true ]]; then - - git clone https://github.com/Kurento/kurento.git - pushd kurento/clients/java - git checkout -f "${KURENTO_JAVA_COMMIT}" - MVN_VERSION=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec) - mvn -B -Dmaven.artifact.threads=1 clean install - popd - rm -rf kurento - mvn -B versions:set-property \ - -Dproperty=version.kurento \ - -DnewVersion="${MVN_VERSION}" - -fi - -# ------------- -# Serve openvidu-testapp -# ------------- -if [[ "${SERVE_OV_TESTAPP}" == true ]]; then - # Generate certificate - openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 \ - -subj "/CN=www.mydom.com/O=My Company LTD./C=US" \ - -keyout /opt/openvidu/testapp/key.pem \ - -out /opt/openvidu/testapp/cert.pem - - # Serve TestApp - pushd /opt/openvidu/testapp - http-server -S -p 4200 &>/opt/openvidu/testapp.log & - popd -fi diff --git a/ci-scripts/kurento-snapshots.xml b/ci-scripts/kurento-snapshots.xml deleted file mode 100644 index 5b765b74..00000000 --- a/ci-scripts/kurento-snapshots.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - default - - true - - - - kurento-github-public - Kurento GitHub Maven packages (public access) - KURENTO_SNAPSHOTS_URL - - false - - - true - - - - - - kurento-github-public - Kurento GitHub Maven packages (public access) - KURENTO_SNAPSHOTS_URL - - false - - - true - - - - - - diff --git a/ci-scripts/openvidu-e2e-tests.sh b/ci-scripts/openvidu-e2e-tests.sh deleted file mode 100755 index ae0d442d..00000000 --- a/ci-scripts/openvidu-e2e-tests.sh +++ /dev/null @@ -1,241 +0,0 @@ -#!/bin/bash -x -set -eu -o pipefail - -############################################################################ -# Any function offered by this file that is not path agnostic assumes that # -# the path is located where the first command of each function requires it # -############################################################################ - -OV_INTEGRATION_TESTS=false -OV_UNIT_TESTS=false -OV_E2E_KURENTO=false -OV_E2E_MEDIASOUP=false -LAUNCH_OV_KURENTO=false -LAUNCH_OV_MEDIASOUP=false - -function environmentLaunch { - local MEDIA_SERVER="$1" - - # Get e2e container id - local E2E_CONTAINER_ID - E2E_CONTAINER_ID="$(docker ps | grep "$TEST_IMAGE" | awk '{ print $1 }')" - - # Get e2e container IP so services running can be accessed by browser and media server containers - local E2E_CONTAINER_IP - E2E_CONTAINER_IP="$(docker inspect "$E2E_CONTAINER_ID" | awk '/bridge/,/IPAddress/' | grep IPAddress | cut -d'"' -f4)" - - # Kurento and mediasoup needs to run as network host, so we need Docker host IP. - local DOCKER_HOST_IP - DOCKER_HOST_IP="$(docker inspect bridge --format '{{with index .IPAM.Config 0}}{{or .Gateway .Subnet}}{{end}}' | sed -r 's|\.0/[[:digit:]]+$|.1|')" - - if [[ "${MEDIA_SERVER}" == "kurento" ]]; then - docker run -e KMS_UID=$(id -u) --network=host --detach=true --volume=/opt/openvidu/recordings:/opt/openvidu/recordings "${KURENTO_MEDIA_SERVER_IMAGE}" - while true; do - RC="$(curl \ - --silent \ - --no-buffer \ - --write-out '%{http_code}' \ - --header "Connection: Upgrade" \ - --header "Upgrade: websocket" \ - --header "Host: ${DOCKER_HOST_IP}" \ - --header "Origin: ${DOCKER_HOST_IP}" \ - "http://${DOCKER_HOST_IP}:8888/kurento" || echo '')" - - if [[ "$RC" == "500" ]]; then - break - else - echo "Waiting for ${MEDIA_SERVER}..." - sleep 1 - fi - done - elif [[ "${MEDIA_SERVER}" == "mediasoup" ]]; then - LOG_DATE=$(printf '%(%Y-%m-%d-%H-%M-%S)T') - docker run --network=host --restart=always \ - --env=KMS_MIN_PORT=40000 \ - --env=KMS_MAX_PORT=65535 \ - --env=OPENVIDU_PRO_LICENSE="${OPENVIDU_PRO_LICENSE}" \ - --env=OPENVIDU_PRO_LICENSE_API="${OPENVIDU_PRO_LICENSE_API}" \ - --env=WEBRTC_LISTENIPS_0_ANNOUNCEDIP="${DOCKER_HOST_IP}" \ - --env=WEBRTC_LISTENIPS_0_IP="${DOCKER_HOST_IP}" \ - --volume=/opt/openvidu/recordings:/opt/openvidu/recordings \ - openvidu/mediasoup-controller:"${MEDIASOUP_CONTROLLER_VERSION}" >& /opt/openvidu/mediasoup-controller-${LOG_DATE}.log & - until $(curl --insecure --output /dev/null --silent http://${DOCKER_HOST_IP}:8888/kurento); do - echo "Waiting for ${MEDIA_SERVER}..." - sleep 1 - done - else - echo "Not valid media server" - exit 1 - fi - - if [ "${DOCKER_RECORDING_VERSION}" != "default" ]; then - echo "Using custom openvidu-recording tag: ${DOCKER_RECORDING_VERSION}" - java -jar -DKMS_URIS="[\"ws://${DOCKER_HOST_IP}:8888/kurento\"]" \ - -DDOMAIN_OR_PUBLIC_IP="${E2E_CONTAINER_IP}" \ - -DOPENVIDU_SECRET=MY_SECRET -DHTTPS_PORT=4443 -DOPENVIDU_RECORDING=true \ - -DOPENVIDU_RECORDING_CUSTOM_LAYOUT=/opt/openvidu/test-layouts \ - -DOPENVIDU_RECORDING_VERSION="${DOCKER_RECORDING_VERSION}" -DOPENVIDU_WEBHOOK=true \ - -DOPENVIDU_WEBHOOK_ENDPOINT=http://127.0.0.1:7777/webhook \ - /opt/openvidu/openvidu-server-*.jar &>/opt/openvidu/openvidu-server-"${MEDIA_SERVER}".log & - else - echo "Using default openvidu-recording tag" - java -jar -DKMS_URIS="[\"ws://${DOCKER_HOST_IP}:8888/kurento\"]" \ - -DDOMAIN_OR_PUBLIC_IP="${E2E_CONTAINER_IP}" \ - -DOPENVIDU_SECRET=MY_SECRET -DHTTPS_PORT=4443 -DOPENVIDU_RECORDING=true \ - -DOPENVIDU_RECORDING_CUSTOM_LAYOUT=/opt/openvidu/test-layouts -DOPENVIDU_WEBHOOK=true \ - -DOPENVIDU_WEBHOOK_ENDPOINT=http://127.0.0.1:7777/webhook \ - /opt/openvidu/openvidu-server-*.jar &>/opt/openvidu/openvidu-server-"${MEDIA_SERVER}".log & - fi - until $(curl --insecure --output /dev/null --silent --head --fail https://OPENVIDUAPP:MY_SECRET@localhost:4443/); do - echo "Waiting for openvidu-server..." - sleep 2 - done -} - -function openviduE2ETests { - local MEDIA_SERVER="$1" - - # Get e2e container id - local E2E_CONTAINER_ID - E2E_CONTAINER_ID="$(docker ps | grep "$TEST_IMAGE" | awk '{ print $1 }')" - - # Get e2e container IP so services running can be accessed by browser and media server containers - local E2E_CONTAINER_IP - E2E_CONTAINER_IP="$(docker inspect "$E2E_CONTAINER_ID" | awk '/bridge/,/IPAddress/' | grep IPAddress | cut -d'"' -f4)" - - # Kurento and mediasoup needs to run as network host, so we need Docker host IP. - local DOCKER_HOST_IP - DOCKER_HOST_IP="$(docker network inspect bridge | grep Subnet | cut -d'"' -f4 | cut -d'/' -f1 | sed 's/.$/1/' | grep 172)" - - pushd openvidu-test-e2e - if [[ "${MEDIA_SERVER}" == "kurento" ]]; then - - mvn -DMEDIA_SERVER_IMAGE="${KURENTO_MEDIA_SERVER_IMAGE}" \ - -DOPENVIDU_URL="https://${E2E_CONTAINER_IP}:4443" \ - -DCHROME_VERSION="${CHROME_VERSION}" \ - -DFIREFOX_VERSION="${FIREFOX_VERSION}" \ - -DEDGE_VERSION="${EDGE_VERSION}" \ - -Dtest=OpenViduTestAppE2eTest \ - -DAPP_URL="https://${E2E_CONTAINER_IP}:4200" \ - -DEXTERNAL_CUSTOM_LAYOUT_URL="http://${E2E_CONTAINER_IP}:4114" \ - -DREMOTE_URL_CHROME="http://${DOCKER_HOST_IP}:6666/wd/hub/" \ - -DREMOTE_URL_FIREFOX="http://${DOCKER_HOST_IP}:6667/wd/hub/" \ - -DREMOTE_URL_OPERA="http://${DOCKER_HOST_IP}:6668/wd/hub/" \ - -DREMOTE_URL_EDGE="http://${DOCKER_HOST_IP}:6669/wd/hub/" \ - -DEXTERNAL_CUSTOM_LAYOUT_PARAMS="sessionId,CUSTOM_LAYOUT_SESSION,secret,MY_SECRET" \ - test - - elif [[ "${MEDIA_SERVER}" == "mediasoup" ]]; then - - mvn -DMEDIA_SERVER_IMAGE="openvidu/mediasoup-controller:${MEDIASOUP_CONTROLLER_VERSION}" \ - -DOPENVIDU_URL="https://${E2E_CONTAINER_IP}:4443" \ - -DCHROME_VERSION="${CHROME_VERSION}" \ - -DFIREFOX_VERSION="${FIREFOX_VERSION}" \ - -DEDGE_VERSION="${EDGE_VERSION}" \ - -Dtest=OpenViduTestAppE2eTest \ - -DAPP_URL="https://${E2E_CONTAINER_IP}:4200" \ - -DEXTERNAL_CUSTOM_LAYOUT_URL="http://${E2E_CONTAINER_IP}:4114" \ - -DREMOTE_URL_CHROME="http://${DOCKER_HOST_IP}:6666/wd/hub/" \ - -DREMOTE_URL_FIREFOX="http://${DOCKER_HOST_IP}:6667/wd/hub/" \ - -DREMOTE_URL_OPERA="http://${DOCKER_HOST_IP}:6668/wd/hub/" \ - -DREMOTE_URL_EDGE="http://${DOCKER_HOST_IP}:6669/wd/hub/" \ - -DEXTERNAL_CUSTOM_LAYOUT_PARAMS="sessionId,CUSTOM_LAYOUT_SESSION,secret,MY_SECRET" \ - -DOPENVIDU_PRO_LICENSE="${OPENVIDU_PRO_LICENSE}" \ - -DOPENVIDU_PRO_LICENSE_API="${OPENVIDU_PRO_LICENSE_API}" \ - test - - else - echo "Not valid media server" - exit 1 - fi - stopMediaServer - kill -9 $(pgrep -f /opt/openvidu/openvidu-server) || true - popd -} - -function stopMediaServer { - # Remove Kurento Media Server - declare -a arr=("kurento/kurento-media-server" - "openvidu/mediasoup-controller:") - for image in "${arr[@]}"; do - docker ps -a | awk '{ print $1,$2 }' | grep "${image}" | awk '{ print $1 }' | xargs -I {} docker rm -f {} || true - done - docker ps -a -} - -# Environment variables -if [[ -n ${1:-} ]]; then - case "${1:-}" in - --openvidu-server-unit-tests) - OV_UNIT_TESTS=true - ;; - --openvidu-server-integration-tests) - OV_INTEGRATION_TESTS=true - ;; - --openvidu-e2e-tests-kurento) - OV_E2E_KURENTO=true - ;; - --openvidu-e2e-tests-mediasoup) - OV_E2E_MEDIASOUP=true - ;; - --environment-launch-kurento) - LAUNCH_OV_KURENTO=true - ;; - --environment-launch-mediasoup) - LAUNCH_OV_MEDIASOUP=true - ;; - *) - echo "Unrecognized method $1" - exit 1 - ;; - esac -else - echo "Must provide a method to execute as first parameter when calling the script" - exit 1 -fi - -# ------------- -# openvidu-server unit tests -# ------------- -if [[ "${OV_UNIT_TESTS}" == true ]]; then - pushd openvidu-server - mvn -B -Dtest=io.openvidu.server.test.unit.*Test test - popd -fi - -# ------------- -# openvidu-server integration tests -# ------------- -if [[ "${OV_INTEGRATION_TESTS}" == true ]]; then - pushd openvidu-server - mvn -B -Dtest=io.openvidu.server.test.integration.*Test test - popd -fi - -# ------------- -# OpenVidu E2E Tests Kurento -# ------------- -if [[ "${OV_E2E_KURENTO}" == true ]]; then - openviduE2ETests "kurento" -fi - -# ------------- -# OpenVidu E2E Tests mediasoup -# ------------- -if [[ "${OV_E2E_MEDIASOUP}" == true ]]; then - openviduE2ETests "mediasoup" -fi - -# ------------- -# Environment launch Kurento -# ------------- -if [[ "${LAUNCH_OV_KURENTO}" == true ]]; then - environmentLaunch "kurento" -fi - -# ------------- -# Environment launch mediasoup -# ------------- -if [[ "${LAUNCH_OV_MEDIASOUP}" == true ]]; then - environmentLaunch "mediasoup" -fi diff --git a/openvidu-browser/.gitignore b/openvidu-browser/.gitignore deleted file mode 100644 index 83622e64..00000000 --- a/openvidu-browser/.gitignore +++ /dev/null @@ -1,62 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Typescript v1 declaration files -typings/ - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env - -docs/ -lib/ -static/** \ No newline at end of file diff --git a/openvidu-browser/.npmignore b/openvidu-browser/.npmignore deleted file mode 100644 index 77f12ae2..00000000 --- a/openvidu-browser/.npmignore +++ /dev/null @@ -1 +0,0 @@ -docs/ diff --git a/openvidu-browser/.prettierrc b/openvidu-browser/.prettierrc deleted file mode 100644 index bc72fe80..00000000 --- a/openvidu-browser/.prettierrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "singleQuote": true, - "printWidth": 140, - "trailingComma": "none", - "semi": true, - "bracketSpacing": true, - "useTabs": false, - "jsxSingleQuote": true, - "tabWidth": 4 -} diff --git a/openvidu-browser/LICENSE b/openvidu-browser/LICENSE deleted file mode 100644 index 261eeb9e..00000000 --- a/openvidu-browser/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/openvidu-browser/config/replace_for_ts44.sh b/openvidu-browser/config/replace_for_ts44.sh deleted file mode 100755 index 87bab506..00000000 --- a/openvidu-browser/config/replace_for_ts44.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -set -e -SEARCH_STRING_1="(type: K," -REPLACE_STRING_1="(type: K | string," -SEARCH_STRING_2='\[key: `signal:\${string}`\]: SignalEvent;' -sed -i "s~${SEARCH_STRING_1}~${REPLACE_STRING_1}~g" ts4.4/lib/OpenVidu/Session.d.ts -sed -i "/${SEARCH_STRING_2}/d" ts4.4/lib/OpenViduInternal/Events/EventMap/SessionEventMap.d.ts \ No newline at end of file diff --git a/openvidu-browser/config/tsconfig.json b/openvidu-browser/config/tsconfig.json deleted file mode 100644 index e7f7ecae..00000000 --- a/openvidu-browser/config/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "include": ["../src"], - "exclude": ["../config", "../docs", "../lib", "../node_modules", "../ts4.4"], - "typedocOptions": { - "name": "OpenVidu Browser", - "entryPoints": ["../src/index.ts"], - "out": "../docs", - "theme": "default", - "readme": "none", - "includeVersion": true, - "validation": { - "notExported": true, - "invalidLink": true - }, - "excludeExternals": true, - "excludePrivate": true, - "excludeProtected": true, - "excludeInternal": true - } -} \ No newline at end of file diff --git a/openvidu-browser/config/tslint.json b/openvidu-browser/config/tslint.json deleted file mode 100644 index c2137e2c..00000000 --- a/openvidu-browser/config/tslint.json +++ /dev/null @@ -1,154 +0,0 @@ -{ - "extends": "tslint:recommended", - "rules": { - "array-type": [ - true, - "array" - ], - "ban-types": { - "options": [ - [ - "Object", - "Avoid using the `Object` type. Did you mean `object`?" - ], - [ - "Function", - "Avoid using the `Function` type. Prefer a specific function type, like `() => void`, or use `ts.AnyFunction`." - ], - [ - "Boolean", - "Avoid using the `Boolean` type. Did you mean `boolean`?" - ], - [ - "Number", - "Avoid using the `Number` type. Did you mean `number`?" - ], - [ - "String", - "Avoid using the `String` type. Did you mean `string`?" - ] - ] - }, - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "curly": [ - true, - "ignore-same-line" - ], - "indent": [ - true, - "spaces", - 2 - ], - "interface-name": [ - true, - "never-prefix" - ], - "interface-over-type-literal": true, - "jsdoc-format": true, - "no-inferrable-types": true, - "no-internal-module": true, - "no-null-keyword": false, - "no-switch-case-fall-through": true, - "no-trailing-whitespace": [ - true, - "ignore-template-strings" - ], - "no-var-keyword": true, - "object-literal-shorthand": true, - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "prefer-const": true, - "quotemark": [ - true, - "single", - "avoid-escape", - "avoid-template" - ], - "semicolon": [ - true, - "always", - "ignore-bound-class-methods" - ], - "space-within-parens": true, - "triple-equals": true, - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }, - { - "call-signature": "onespace", - "index-signature": "onespace", - "parameter": "onespace", - "property-declaration": "onespace", - "variable-declaration": "onespace" - } - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-module", - "check-separator", - "check-type" - ], - "no-implicit-dependencies": [ - true, - "dev" - ], - "object-literal-key-quotes": [ - true, - "consistent-as-needed" - ], - "variable-name": [ - true, - "ban-keywords", - "check-format", - "allow-leading-underscore" - ], - "arrow-parens": false, - "arrow-return-shorthand": false, - "forin": false, - "member-access": false, - "no-conditional-assignment": false, - "no-console": false, - "no-debugger": false, - "no-empty-interface": false, - "no-eval": false, - "no-object-literal-type-assertion": false, - "no-shadowed-variable": false, - "no-submodule-imports": false, - "no-var-requires": false, - "ordered-imports": false, - "prefer-conditional-expression": false, - "radix": false, - "trailing-comma": false, - "align": false, - "eofline": false, - "max-line-length": false, - "no-consecutive-blank-lines": false, - "space-before-function-paren": false, - "ban-comma-operator": false, - "max-classes-per-file": false, - "member-ordering": false, - "no-angle-bracket-type-assertion": false, - "no-bitwise": false, - "no-namespace": false, - "no-reference": false, - "object-literal-sort-keys": false, - "one-variable-per-declaration": false, - "unified-signatures": false - } -} \ No newline at end of file diff --git a/openvidu-browser/generate-docs.sh b/openvidu-browser/generate-docs.sh deleted file mode 100755 index 4b75d815..00000000 --- a/openvidu-browser/generate-docs.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -if [[ -z "$BASEHREF_VERSION" ]]; then - echo "Example of use: \"BASEHREF_VERSION=2.12.0 ${0}\"" 1>&2 - exit 1 -fi - -# Replace version from "stable" to the specified one in all TypeDoc links -grep -rl '/en/stable/' src | xargs sed -i -e 's|/en/stable/|/en/'${BASEHREF_VERSION}'/|g' - -# Generate TypeDoc -./node_modules/typedoc/bin/typedoc --tsconfig ./config/tsconfig.json - -# Return links to "stable" version -grep -rl '/en/'${BASEHREF_VERSION}'/' src | xargs sed -i -e 's|/en/'${BASEHREF_VERSION}'/|/en/stable/|g' - -# Clean previous docs from openvidu.io-docs repo and copy new ones -rm -rf ../../openvidu.io-docs/docs/api/openvidu-browser/* -cp -R ./docs/. ../../openvidu.io-docs/docs/api/openvidu-browser \ No newline at end of file diff --git a/openvidu-browser/package-lock.json b/openvidu-browser/package-lock.json deleted file mode 100644 index 604c87f9..00000000 --- a/openvidu-browser/package-lock.json +++ /dev/null @@ -1,2511 +0,0 @@ -{ - "name": "openvidu-browser", - "version": "2.30.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "openvidu-browser", - "version": "2.30.0", - "license": "Apache-2.0", - "dependencies": { - "events": "3.3.0", - "freeice": "2.2.2", - "hark": "1.2.3", - "inherits": "2.0.4", - "jsnlog": "2.30.0", - "mime": "3.0.0", - "platform": "1.3.6", - "semver": "7.6.2", - "uuid": "9.0.1", - "wolfy87-eventemitter": "5.2.9" - }, - "devDependencies": { - "@types/node": "18.11.9", - "@types/platform": "1.3.4", - "browserify": "17.0.0", - "terser": "5.15.1", - "tsify": "5.0.4", - "tslint": "6.1.3", - "typedoc": "0.23.21", - "typescript": "4.9.3" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.24.2", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", - "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", - "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.5", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@types/node": { - "version": "18.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", - "dev": true - }, - "node_modules/@types/platform": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@types/platform/-/platform-1.3.4.tgz", - "integrity": "sha512-U0o4K+GNiK0PNxoDwd8xRnvLVe4kzei6opn3/FCjAriqaP+rfrDdSl1kP/hLL6Y3/Y3hhGnBwD4dCkkAqs1W/Q==", - "dev": true - }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-node": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", - "dev": true, - "dependencies": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - } - }, - "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "dependencies": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/asn1.js/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/assert": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.1.tgz", - "integrity": "sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==", - "dev": true, - "dependencies": { - "object.assign": "^4.1.4", - "util": "^0.10.4" - } - }, - "node_modules/assert/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true - }, - "node_modules/assert/node_modules/util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", - "dev": true, - "dependencies": { - "inherits": "2.0.3" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "dev": true - }, - "node_modules/browser-pack": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", - "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", - "dev": true, - "dependencies": { - "combine-source-map": "~0.8.0", - "defined": "^1.0.0", - "JSONStream": "^1.0.3", - "safe-buffer": "^5.1.1", - "through2": "^2.0.0", - "umd": "^3.0.0" - }, - "bin": { - "browser-pack": "bin/cmd.js" - } - }, - "node_modules/browser-resolve": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", - "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", - "dev": true, - "dependencies": { - "resolve": "^1.17.0" - } - }, - "node_modules/browserify": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/browserify/-/browserify-17.0.0.tgz", - "integrity": "sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w==", - "dev": true, - "dependencies": { - "assert": "^1.4.0", - "browser-pack": "^6.0.1", - "browser-resolve": "^2.0.0", - "browserify-zlib": "~0.2.0", - "buffer": "~5.2.1", - "cached-path-relative": "^1.0.0", - "concat-stream": "^1.6.0", - "console-browserify": "^1.1.0", - "constants-browserify": "~1.0.0", - "crypto-browserify": "^3.0.0", - "defined": "^1.0.0", - "deps-sort": "^2.0.1", - "domain-browser": "^1.2.0", - "duplexer2": "~0.1.2", - "events": "^3.0.0", - "glob": "^7.1.0", - "has": "^1.0.0", - "htmlescape": "^1.1.0", - "https-browserify": "^1.0.0", - "inherits": "~2.0.1", - "insert-module-globals": "^7.2.1", - "JSONStream": "^1.0.3", - "labeled-stream-splicer": "^2.0.0", - "mkdirp-classic": "^0.5.2", - "module-deps": "^6.2.3", - "os-browserify": "~0.3.0", - "parents": "^1.0.1", - "path-browserify": "^1.0.0", - "process": "~0.11.0", - "punycode": "^1.3.2", - "querystring-es3": "~0.2.0", - "read-only-stream": "^2.0.0", - "readable-stream": "^2.0.2", - "resolve": "^1.1.4", - "shasum-object": "^1.0.0", - "shell-quote": "^1.6.1", - "stream-browserify": "^3.0.0", - "stream-http": "^3.0.0", - "string_decoder": "^1.1.1", - "subarg": "^1.0.0", - "syntax-error": "^1.1.1", - "through2": "^2.0.0", - "timers-browserify": "^1.0.1", - "tty-browserify": "0.0.1", - "url": "~0.11.0", - "util": "~0.12.0", - "vm-browserify": "^1.0.0", - "xtend": "^4.0.0" - }, - "bin": { - "browserify": "bin/cmd.js" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "dependencies": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "dependencies": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "node_modules/browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "dependencies": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/browserify-rsa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", - "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", - "dev": true, - "dependencies": { - "bn.js": "^5.0.0", - "randombytes": "^2.0.1" - } - }, - "node_modules/browserify-sign": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", - "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", - "dev": true, - "dependencies": { - "bn.js": "^5.2.1", - "browserify-rsa": "^4.1.0", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.5", - "hash-base": "~3.0", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.7", - "readable-stream": "^2.3.8", - "safe-buffer": "^5.2.1" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "dependencies": { - "pako": "~1.0.5" - } - }, - "node_modules/buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", - "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", - "dev": true, - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "dev": true - }, - "node_modules/builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", - "dev": true - }, - "node_modules/cached-path-relative": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.1.0.tgz", - "integrity": "sha512-WF0LihfemtesFcJgO7xfOoOcnWzY/QHR4qeDqV44jPU3HTI54+LnfXK3SA27AVVGCdZFgjjFFaqUA9Jx7dMJZA==", - "dev": true - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/combine-source-map": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", - "integrity": "sha512-UlxQ9Vw0b/Bt/KYwCFqdEwsQ1eL8d1gibiFb7lxQJFdvTgc2hIZi6ugsg+kyhzhPV+QEpUiEIwInIAIrgoEkrg==", - "dev": true, - "dependencies": { - "convert-source-map": "~1.1.0", - "inline-source-map": "~0.6.0", - "lodash.memoize": "~3.0.3", - "source-map": "~0.5.3" - } - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/console-browserify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", - "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", - "dev": true - }, - "node_modules/constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", - "dev": true - }, - "node_modules/convert-source-map": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", - "integrity": "sha512-Y8L5rp6jo+g9VEPgvqNfEopjTR4OTYct8lXlS8iVQdmnjDvbdbzYe9rjtFCB9egC86JoNCU61WRY+ScjkZpnIg==", - "dev": true - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, - "node_modules/create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", - "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "elliptic": "^6.5.3" - } - }, - "node_modules/create-ecdh/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "node_modules/create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "dependencies": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "node_modules/crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "dependencies": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - }, - "engines": { - "node": "*" - } - }, - "node_modules/dash-ast": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", - "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", - "dev": true - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/defined": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", - "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/deps-sort": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz", - "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==", - "dev": true, - "dependencies": { - "JSONStream": "^1.0.3", - "shasum-object": "^1.0.0", - "subarg": "^1.0.0", - "through2": "^2.0.0" - }, - "bin": { - "deps-sort": "bin/cmd.js" - } - }, - "node_modules/des.js": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", - "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/detective": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", - "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", - "dev": true, - "dependencies": { - "acorn-node": "^1.8.2", - "defined": "^1.0.0", - "minimist": "^1.2.6" - }, - "bin": { - "detective": "bin/detective.js" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "node_modules/diffie-hellman/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true, - "engines": { - "node": ">=0.4", - "npm": ">=1.2" - } - }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", - "dev": true, - "dependencies": { - "readable-stream": "^2.0.2" - } - }, - "node_modules/elliptic": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz", - "integrity": "sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==", - "dev": true, - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "dependencies": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "dev": true - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/freeice": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/freeice/-/freeice-2.2.2.tgz", - "integrity": "sha512-XNoIxDHufqPIBSLpp4IrFPnoc+hv/0RwdOGhIoggIDC2ZKf5r6OoixbeoFJSmZOAq2aYiEUArhuQ8zVVrM5C4w==", - "dependencies": { - "normalice": "^1.0.0" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-assigned-identifiers": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", - "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", - "dev": true - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hark": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/hark/-/hark-1.2.3.tgz", - "integrity": "sha512-u68vz9SCa38ESiFJSDjqK8XbXqWzyot7Cj6Y2b6jk2NJ+II3MY2dIrLMg/kjtIAun4Y1DHF/20hfx4rq1G5GMg==", - "dependencies": { - "wildemitter": "^1.2.0" - } - }, - "node_modules/has": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", - "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "dev": true, - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/htmlescape": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", - "integrity": "sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", - "dev": true - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/inline-source-map": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.3.tgz", - "integrity": "sha512-1aVsPEsJWMJq/pdMU61CDlm1URcW702MTB4w9/zUjMus6H/Py8o7g68Pr9D4I6QluWGt/KdmswuRhaA05xVR1w==", - "dev": true, - "dependencies": { - "source-map": "~0.5.3" - } - }, - "node_modules/insert-module-globals": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", - "integrity": "sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==", - "dev": true, - "dependencies": { - "acorn-node": "^1.5.2", - "combine-source-map": "^0.8.0", - "concat-stream": "^1.6.1", - "is-buffer": "^1.1.0", - "JSONStream": "^1.0.3", - "path-is-absolute": "^1.0.1", - "process": "~0.11.0", - "through2": "^2.0.0", - "undeclared-identifiers": "^1.1.2", - "xtend": "^4.0.0" - }, - "bin": { - "insert-module-globals": "bin/cmd.js" - } - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", - "dev": true - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsnlog": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/jsnlog/-/jsnlog-2.30.0.tgz", - "integrity": "sha512-o3ROQVkhek+dkc7/9TXlB4TNtxUpYsRLOBJHZYk3Vy0B5zRBmfv9tyr56PrjcgEXuy06ARgfLTANY0+ImhzzGA==" - }, - "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true - }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "dev": true, - "engines": [ - "node >= 0.2.0" - ] - }, - "node_modules/JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/labeled-stream-splicer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz", - "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "stream-splicer": "^2.0.0" - } - }, - "node_modules/lodash.memoize": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", - "integrity": "sha512-eDn9kqrAmVUC1wmZvlQ6Uhde44n+tXpqPrN8olQJbttgh0oKclk+SF54P47VEGE9CEiMeRwAP8BaM7UHvBkz2A==", - "dev": true - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, - "node_modules/marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", - "dev": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "dependencies": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, - "bin": { - "miller-rabin": "bin/miller-rabin" - } - }, - "node_modules/miller-rabin/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "dev": true - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true - }, - "node_modules/module-deps": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz", - "integrity": "sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==", - "dev": true, - "dependencies": { - "browser-resolve": "^2.0.0", - "cached-path-relative": "^1.0.2", - "concat-stream": "~1.6.0", - "defined": "^1.0.0", - "detective": "^5.2.0", - "duplexer2": "^0.1.2", - "inherits": "^2.0.1", - "JSONStream": "^1.0.3", - "parents": "^1.0.0", - "readable-stream": "^2.0.2", - "resolve": "^1.4.0", - "stream-combiner2": "^1.1.1", - "subarg": "^1.0.0", - "through2": "^2.0.0", - "xtend": "^4.0.0" - }, - "bin": { - "module-deps": "bin/cmd.js" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/normalice": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/normalice/-/normalice-1.0.1.tgz", - "integrity": "sha512-wF2/tv9q/K8S+RqCgll5yC6z/zcXNr+rEHfGIw8A6D58vjfJo+kp749MI6cAHv72LE7nwv92Qi6tZhIeMOOJpg==" - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", - "dev": true - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "node_modules/parents": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", - "integrity": "sha512-mXKF3xkoUt5td2DoxpLmtOmZvko9VfFpwRwkKDHSNvgmpLAeBo18YDhcPbBzJq+QLCHMbGOfzia2cX4U+0v9Mg==", - "dev": true, - "dependencies": { - "path-platform": "~0.11.15" - } - }, - "node_modules/parse-asn1": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", - "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", - "dev": true, - "dependencies": { - "asn1.js": "^4.10.1", - "browserify-aes": "^1.2.0", - "evp_bytestokey": "^1.0.3", - "hash-base": "~3.0", - "pbkdf2": "^3.1.2", - "safe-buffer": "^5.2.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", - "dev": true, - "dependencies": { - "error-ex": "^1.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-platform": { - "version": "0.11.15", - "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", - "integrity": "sha512-Y30dB6rab1A/nfEKsZxmr01nUotHX0c/ZiIAsCTatEe1CmS5Pm5He7fZ195bPT7RdquoaL8lLxFCMQi/bS7IJg==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "dev": true, - "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true - }, - "node_modules/platform": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" - }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "node_modules/public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true - }, - "node_modules/qs": { - "version": "6.12.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", - "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", - "dev": true, - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "dependencies": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "node_modules/read-only-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", - "integrity": "sha512-3ALe0bjBVZtkdWKIcThYpQCLbBMd/+Tbh2CDSrAIDO3UsZ4Xs+tnyjv2MjCOMMgBG+AsUOeuP1cgtY1INISc8w==", - "dev": true, - "dependencies": { - "readable-stream": "^2.0.2" - } - }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/readable-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "bin": { - "sha.js": "bin.js" - } - }, - "node_modules/shasum-object": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", - "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", - "dev": true, - "dependencies": { - "fast-safe-stringify": "^2.0.7" - } - }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/shiki": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.11.1.tgz", - "integrity": "sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA==", - "dev": true, - "dependencies": { - "jsonc-parser": "^3.0.0", - "vscode-oniguruma": "^1.6.1", - "vscode-textmate": "^6.0.0" - } - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stream-browserify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", - "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", - "dev": true, - "dependencies": { - "inherits": "~2.0.4", - "readable-stream": "^3.5.0" - } - }, - "node_modules/stream-browserify/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/stream-combiner2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", - "dev": true, - "dependencies": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" - } - }, - "node_modules/stream-http": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", - "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", - "dev": true, - "dependencies": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "xtend": "^4.0.2" - } - }, - "node_modules/stream-http/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/stream-splicer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz", - "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.2" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", - "dev": true, - "dependencies": { - "is-utf8": "^0.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/subarg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", - "integrity": "sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==", - "dev": true, - "dependencies": { - "minimist": "^1.1.0" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/syntax-error": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", - "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", - "dev": true, - "dependencies": { - "acorn-node": "^1.2.0" - } - }, - "node_modules/terser": { - "version": "5.15.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz", - "integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==", - "dev": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser/node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/timers-browserify": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", - "integrity": "sha512-PIxwAupJZiYU4JmVZYwXp9FKsHMXb5h0ZEFyuXTAn8WLHOlcij+FEcbrvDsom1o5dr1YggEtFbECvGCW2sT53Q==", - "dev": true, - "dependencies": { - "process": "~0.11.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/tsconfig": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-5.0.3.tgz", - "integrity": "sha512-Cq65A3kVp6BbsUgg9DRHafaGmbMb9EhAc7fjWvudNWKjkbWrt43FnrtZt6awshH1R0ocfF2Z0uxock3lVqEgOg==", - "dev": true, - "dependencies": { - "any-promise": "^1.3.0", - "parse-json": "^2.2.0", - "strip-bom": "^2.0.0", - "strip-json-comments": "^2.0.0" - } - }, - "node_modules/tsify": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/tsify/-/tsify-5.0.4.tgz", - "integrity": "sha512-XAZtQ5OMPsJFclkZ9xMZWkSNyMhMxEPsz3D2zu79yoKorH9j/DT4xCloJeXk5+cDhosEibu4bseMVjyPOAyLJA==", - "dev": true, - "dependencies": { - "convert-source-map": "^1.1.0", - "fs.realpath": "^1.0.0", - "object-assign": "^4.1.0", - "semver": "^6.1.0", - "through2": "^2.0.0", - "tsconfig": "^5.0.3" - }, - "engines": { - "node": ">=0.12" - }, - "peerDependencies": { - "browserify": ">= 10.x", - "typescript": ">= 2.8" - } - }, - "node_modules/tsify/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tslint": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", - "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", - "deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.3", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.13.0", - "tsutils": "^2.29.0" - }, - "bin": { - "tslint": "bin/tslint" - }, - "engines": { - "node": ">=4.8.0" - }, - "peerDependencies": { - "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" - } - }, - "node_modules/tslint/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "peerDependencies": { - "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" - } - }, - "node_modules/tty-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", - "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", - "dev": true - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "dev": true - }, - "node_modules/typedoc": { - "version": "0.23.21", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.21.tgz", - "integrity": "sha512-VNE9Jv7BgclvyH9moi2mluneSviD43dCE9pY8RWkO88/DrEgJZk9KpUk7WO468c9WWs/+aG6dOnoH7ccjnErhg==", - "dev": true, - "dependencies": { - "lunr": "^2.3.9", - "marked": "^4.0.19", - "minimatch": "^5.1.0", - "shiki": "^0.11.1" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 14.14" - }, - "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x" - } - }, - "node_modules/typedoc/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/typedoc/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/typescript": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/umd": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", - "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", - "dev": true, - "bin": { - "umd": "bin/cli.js" - } - }, - "node_modules/undeclared-identifiers": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", - "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", - "dev": true, - "dependencies": { - "acorn-node": "^1.3.0", - "dash-ast": "^1.0.0", - "get-assigned-identifiers": "^1.2.0", - "simple-concat": "^1.0.0", - "xtend": "^4.0.1" - }, - "bin": { - "undeclared-identifiers": "bin.js" - } - }, - "node_modules/url": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz", - "integrity": "sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==", - "dev": true, - "dependencies": { - "punycode": "^1.4.1", - "qs": "^6.11.2" - } - }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true - }, - "node_modules/vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", - "dev": true - }, - "node_modules/vscode-textmate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-6.0.0.tgz", - "integrity": "sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ==", - "dev": true - }, - "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wildemitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/wildemitter/-/wildemitter-1.2.1.tgz", - "integrity": "sha512-UMmSUoIQSir+XbBpTxOTS53uJ8s/lVhADCkEbhfRjUGFDPme/XGOb0sBWLx5sTz7Wx/2+TlAw1eK9O5lw5PiEw==" - }, - "node_modules/wolfy87-eventemitter": { - "version": "5.2.9", - "resolved": "https://registry.npmjs.org/wolfy87-eventemitter/-/wolfy87-eventemitter-5.2.9.tgz", - "integrity": "sha512-P+6vtWyuDw+MB01X7UeF8TaHBvbCovf4HPEMF/SV7BdDc1SMTiBy13SRD71lQh4ExFTG1d/WNzDGDCyOKSMblw==" - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "engines": { - "node": ">=0.4" - } - } - } -} diff --git a/openvidu-browser/package.json b/openvidu-browser/package.json deleted file mode 100644 index 2a9d46f0..00000000 --- a/openvidu-browser/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "author": "OpenVidu", - "dependencies": { - "events": "3.3.0", - "freeice": "2.2.2", - "hark": "1.2.3", - "inherits": "2.0.4", - "jsnlog": "2.30.0", - "mime": "3.0.0", - "platform": "1.3.6", - "semver": "7.6.2", - "uuid": "9.0.1", - "wolfy87-eventemitter": "5.2.9" - }, - "description": "OpenVidu Browser", - "devDependencies": { - "@types/node": "18.11.9", - "@types/platform": "1.3.4", - "browserify": "17.0.0", - "terser": "5.15.1", - "tsify": "5.0.4", - "tslint": "6.1.3", - "typedoc": "0.23.21", - "typescript": "4.9.3" - }, - "license": "Apache-2.0", - "main": "lib/index.js", - "name": "openvidu-browser", - "repository": { - "type": "git", - "url": "git://github.com/OpenVidu/openvidu" - }, - "scripts": { - "browserify": "VERSION=${VERSION:-dev}; mkdir -p static/js/ && cd src && ../node_modules/browserify/bin/cmd.js Main.ts -p [ tsify ] --exclude kurento-browser-extensions --debug -o ../static/js/openvidu-browser-$VERSION.js -v", - "browserify-prod": "VERSION=${VERSION:-dev}; mkdir -p static/js/ && cd src && ../node_modules/browserify/bin/cmd.js --debug Main.ts -p [ tsify ] --exclude kurento-browser-extensions | ../node_modules/terser/bin/terser --source-map content=inline --output ../static/js/openvidu-browser-$VERSION.min.js", - "build": "cd src/OpenVidu && ./../../node_modules/typescript/bin/tsc && cd ../.. && ./node_modules/typescript/bin/tsc --declaration src/index.ts --outDir ./lib --sourceMap --target es5 --lib dom,es5,es2015.promise,scripthost && rm -rf ./ts4.4 && mkdir -p ./ts4.4/lib && cp -r ./lib ./ts4.4 && find ./ts4.4/lib -type f ! -iname '*.d.ts' -delete && ./config/replace_for_ts44.sh", - "docs": "./generate-docs.sh" - }, - "types": "lib/index.d.ts", - "typesVersions": { - "<4.4": { - "*": [ - "ts4.4/*" - ] - } - }, - "version": "2.30.0" -} diff --git a/openvidu-browser/src/Main.ts b/openvidu-browser/src/Main.ts deleted file mode 100644 index dfc88ff4..00000000 --- a/openvidu-browser/src/Main.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { OpenVidu } from './OpenVidu/OpenVidu'; -import { JL } from 'jsnlog'; - -if (typeof globalThis !== 'undefined') { - globalThis['OpenVidu'] = OpenVidu; -} - -// Disable jsnlog when library is loaded -JL.setOptions({ enabled: false }); diff --git a/openvidu-browser/src/OpenVidu/Connection.ts b/openvidu-browser/src/OpenVidu/Connection.ts deleted file mode 100644 index 39af8182..00000000 --- a/openvidu-browser/src/OpenVidu/Connection.ts +++ /dev/null @@ -1,217 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Session } from './Session'; -import { Stream } from './Stream'; -import { LocalConnectionOptions } from '../OpenViduInternal/Interfaces/Private/LocalConnectionOptions'; -import { RemoteConnectionOptions } from '../OpenViduInternal/Interfaces/Private/RemoteConnectionOptions'; -import { InboundStreamOptions } from '../OpenViduInternal/Interfaces/Private/InboundStreamOptions'; -import { StreamOptionsServer } from '../OpenViduInternal/Interfaces/Private/StreamOptionsServer'; -import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; -import { ExceptionEvent, ExceptionEventName } from '../OpenViduInternal/Events/ExceptionEvent'; - -/** - * @hidden - */ -const logger: OpenViduLogger = OpenViduLogger.getInstance(); - -/** - * Represents each one of the user's connection to the session (the local one and other user's connections). - * Therefore each {@link Session} and {@link Stream} object has an attribute of type Connection - */ -export class Connection { - /** - * Unique identifier of the connection - */ - connectionId: string; - - /** - * Time when this connection was created in OpenVidu Server (UTC milliseconds) - */ - creationTime: number; - - /** - * Data associated to this connection (and therefore to certain user). This is an important field: - * it allows you to broadcast all the information you want for each user (a username, for example) - */ - data: string; - - /** - * Role of the connection. - * - `SUBSCRIBER`: can subscribe to published Streams of other users by calling {@link Session.subscribe} - * - `PUBLISHER`: SUBSCRIBER permissions + can publish their own Streams by calling {@link Session.publish} - * - `MODERATOR`: SUBSCRIBER + PUBLISHER permissions + can force the unpublishing or disconnection over a third-party Stream or Connection by call {@link Session.forceUnpublish} and {@link Session.forceDisconnect} - * - * **Only defined for the local connection. In remote connections will be `undefined`** - */ - role: string; - - /** - * Whether the streams published by this Connection will be recorded or not. This only affects [INDIVIDUAL recording](/en/stable/advanced-features/recording/#individual-recording-selection) PRO - * - * **Only defined for the local connection. In remote connections will be `undefined`** - */ - record: boolean; - - /** - * @hidden - */ - stream?: Stream; - - /** - * @hidden - */ - localOptions: LocalConnectionOptions | undefined; - - /** - * @hidden - */ - remoteOptions: RemoteConnectionOptions | undefined; - - /** - * @hidden - */ - disposed = false; - - /** - * @hidden - */ - rpcSessionId: string; - - /** - * @hidden - */ - constructor(private session: Session, connectionOptions: LocalConnectionOptions | RemoteConnectionOptions) { - let msg = "'Connection' created "; - if (!!(connectionOptions).role) { - // Connection is local - this.localOptions = connectionOptions; - this.connectionId = this.localOptions.id; - this.creationTime = this.localOptions.createdAt; - this.data = this.localOptions.metadata; - this.rpcSessionId = this.localOptions.sessionId; - this.role = this.localOptions.role; - this.record = this.localOptions.record; - msg += '(local)'; - } else { - // Connection is remote - this.remoteOptions = connectionOptions; - this.connectionId = this.remoteOptions.id; - this.creationTime = this.remoteOptions.createdAt; - if (this.remoteOptions.metadata) { - this.data = this.remoteOptions.metadata; - } - if (this.remoteOptions.streams) { - this.initRemoteStreams(this.remoteOptions.streams); - } - msg += "(remote) with 'connectionId' [" + this.remoteOptions.id + ']'; - } - logger.info(msg); - } - - /* Hidden methods */ - - /** - * @hidden - */ - sendIceCandidate(candidate: RTCIceCandidate): void { - - if (!this.disposed) { - logger.debug((!!this.stream!.outboundStreamOpts ? 'Local' : 'Remote') + 'candidate for' + this.connectionId, candidate); - - this.session.openvidu.sendRequest( - 'onIceCandidate', - { - endpointName: this.connectionId, - candidate: candidate.candidate, - sdpMid: candidate.sdpMid, - sdpMLineIndex: candidate.sdpMLineIndex - }, - (error, response) => { - if (error) { - logger.error('Error sending ICE candidate: ' + JSON.stringify(error)); - this.session.emitEvent('exception', [ - new ExceptionEvent( - this.session, - ExceptionEventName.ICE_CANDIDATE_ERROR, - this.session, - 'There was an unexpected error on the server-side processing an ICE candidate generated and sent by the client-side', - error - ) - ]); - } - } - ); - } else { - logger.warn(`Connection ${this.connectionId} disposed when trying to send an ICE candidate. ICE candidate not sent`); - } - } - - /** - * @hidden - */ - initRemoteStreams(options: StreamOptionsServer[]): void { - // This is ready for supporting multiple streams per Connection object. Right now the loop will always run just once - // this.stream should also be replaced by a collection of streams to support multiple streams per Connection - options.forEach((opts) => { - const streamOptions: InboundStreamOptions = { - id: opts.id, - createdAt: opts.createdAt, - connection: this, - hasAudio: opts.hasAudio, - hasVideo: opts.hasVideo, - audioActive: opts.audioActive, - videoActive: opts.videoActive, - typeOfVideo: opts.typeOfVideo, - frameRate: opts.frameRate, - videoDimensions: !!opts.videoDimensions ? JSON.parse(opts.videoDimensions) : undefined, - filter: !!opts.filter ? opts.filter : undefined - }; - const stream = new Stream(this.session, streamOptions); - - this.addStream(stream); - }); - - logger.info( - "Remote 'Connection' with 'connectionId' [" + this.connectionId + '] is now configured for receiving Streams with options: ', - this.stream!.inboundStreamOpts - ); - } - - /** - * @hidden - */ - addStream(stream: Stream): void { - stream.connection = this; - this.stream = stream; - } - - /** - * @hidden - */ - removeStream(): void { - delete this.stream; - } - - /** - * @hidden - */ - dispose(): void { - this.disposed = true; - this.removeStream(); - } -} diff --git a/openvidu-browser/src/OpenVidu/EventDispatcher.ts b/openvidu-browser/src/OpenVidu/EventDispatcher.ts deleted file mode 100644 index dde7135b..00000000 --- a/openvidu-browser/src/OpenVidu/EventDispatcher.ts +++ /dev/null @@ -1,112 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Event } from '../OpenViduInternal/Events/Event'; -import { EventMap } from '../OpenViduInternal/Events/EventMap/EventMap'; -import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; - -import EventEmitter = require('wolfy87-eventemitter'); - -/** - * @hidden - */ -const logger: OpenViduLogger = OpenViduLogger.getInstance(); - -export abstract class EventDispatcher { - /** - * @hidden - */ - userHandlerArrowHandler: WeakMap<(event: Event) => void, (event: Event) => void> = new WeakMap(); - /** - * @hidden - */ - ee = new EventEmitter(); - - /** - * Adds function `handler` to handle event `type` - * - * @returns The EventDispatcher object - */ - abstract on(type: K, handler: (event: EventMap[K]) => void): this; - - /** - * Adds function `handler` to handle event `type` just once. The handler will be automatically removed after first execution - * - * @returns The object that dispatched the event - */ - abstract once(type: K, handler: (event: EventMap[K]) => void): this; - - /** - * Removes a `handler` from event `type`. If no handler is provided, all handlers will be removed from the event - * - * @returns The object that dispatched the event - */ - abstract off(type: K, handler?: (event: EventMap[K]) => void): this; - - /** - * @hidden - */ - onAux(type: string, message: string, handler: (event: Event) => void): EventDispatcher { - const arrowHandler = (event) => { - if (event) { - logger.debug(message, event); - } else { - logger.debug(message); - } - handler(event); - }; - this.userHandlerArrowHandler.set(handler, arrowHandler); - this.ee.on(type, arrowHandler); - return this; - } - - /** - * @hidden - */ - onceAux(type: string, message: string, handler: (event: Event) => void): EventDispatcher { - const arrowHandler = (event) => { - if (event) { - logger.debug(message, event); - } else { - logger.debug(message); - } - handler(event); - // Remove handler from map after first and only execution - this.userHandlerArrowHandler.delete(handler); - }; - this.userHandlerArrowHandler.set(handler, arrowHandler); - this.ee.once(type, arrowHandler); - return this; - } - - /** - * @hidden - */ - offAux(type: string, handler?: (event: Event) => void): EventDispatcher { - if (!handler) { - this.ee.removeAllListeners(type); - } else { - // Must remove internal arrow function handler paired with user handler - const arrowHandler = this.userHandlerArrowHandler.get(handler); - if (!!arrowHandler) { - this.ee.off(type, arrowHandler); - } - this.userHandlerArrowHandler.delete(handler); - } - return this; - } -} diff --git a/openvidu-browser/src/OpenVidu/Filter.ts b/openvidu-browser/src/OpenVidu/Filter.ts deleted file mode 100644 index 5e339733..00000000 --- a/openvidu-browser/src/OpenVidu/Filter.ts +++ /dev/null @@ -1,285 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Stream } from './Stream'; -import { FilterEvent } from '../OpenViduInternal/Events/FilterEvent'; -import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent'; -import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError'; -import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; - -/** - * @hidden - */ -const logger: OpenViduLogger = OpenViduLogger.getInstance(); - -/** - * **WARNING**: experimental option. This interface may change in the near future - * - * Video/audio filter applied to a Stream. See {@link Stream.applyFilter} - */ -export class Filter { - /** - * Type of filter applied. This is the name of the remote class identifying the filter to apply in Kurento Media Server. - * For example: `"FaceOverlayFilter"`, `"GStreamerFilter"`. - * - * You can get this property in `*.kmd.json` files defining the Kurento filters. For example, for GStreamerFilter that's - * [here](https://github.com/Kurento/kms-filters/blob/53a452fac71d61795952e3d2202156c6b00f6d65/src/server/interface/filters.GStreamerFilter.kmd.json#L4) - */ - type: string; - - /** - * Parameters used to initialize the filter. - * These correspond to the constructor parameters used in the filter in Kurento Media Server (except for `mediaPipeline` parameter, which is never needed). - * - * For example: for `filter.type = "GStreamerFilter"` could be `filter.options = {"command": "videobalance saturation=0.0"}` - * - * You can get this property in `*.kmd.json` files defining the Kurento filters. For example, for GStreamerFilter that's - * [here](https://github.com/Kurento/kms-filters/blob/53a452fac71d61795952e3d2202156c6b00f6d65/src/server/interface/filters.GStreamerFilter.kmd.json#L13-L31) - */ - options: Object; - - /** - * Value passed the last time {@link Filter.execMethod} was called. If `undefined` this method has not been called yet. - * - * You can use this value to know the current status of any applied filter - */ - lastExecMethod?: { - method: string; - params: Object; - }; - - /** - * @hidden - */ - handlers: Map void> = new Map(); - - /** - * @hidden - */ - stream: Stream; - private logger: OpenViduLogger; - - /** - * @hidden - */ - constructor(type: string, options: Object) { - this.type = type; - this.options = options; - } - - /** - * Executes a filter method. Available methods are specific for each filter - * - * @param method Name of the method - * @param params Parameters of the method - */ - execMethod(method: string, params: Object): Promise { - return new Promise((resolve, reject) => { - logger.info('Executing filter method to stream ' + this.stream.streamId); - - let finalParams; - - const successExecMethod = (triggerEvent) => { - logger.info('Filter method successfully executed on Stream ' + this.stream.streamId); - const oldValue = (Object).assign({}, this.stream.filter); - this.stream.filter!.lastExecMethod = { method, params: finalParams }; - if (triggerEvent) { - this.stream.session.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent( - this.stream.session, - this.stream, - 'filter', - this.stream.filter!, - oldValue, - 'execFilterMethod' - ) - ]); - this.stream.streamManager.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent( - this.stream.streamManager, - this.stream, - 'filter', - this.stream.filter!, - oldValue, - 'execFilterMethod' - ) - ]); - } - return resolve(); - }; - - if (this.type.startsWith('VB:')) { - if (typeof params === 'string') { - try { - params = JSON.parse(params); - } catch (error) { - return reject(new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, 'Wrong params syntax: ' + error)); - } - } - - finalParams = params; - - if (method === 'update') { - if (!this.stream.virtualBackgroundSinkElements?.VB) { - return reject( - new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, 'There is no Virtual Background filter applied') - ); - } else { - this.stream.virtualBackgroundSinkElements.VB.updateValues(params) - .then(() => successExecMethod(false)) - .catch((error) => { - if (error.name === OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR) { - return reject(new OpenViduError(error.name, error.message)); - } else { - return reject( - new OpenViduError( - OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, - 'Error updating values on Virtual Background filter: ' + error - ) - ); - } - }); - } - } else { - return reject( - new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, `Unknown Virtual Background method "${method}"`) - ); - } - } else { - let stringParams; - if (typeof params !== 'string') { - try { - stringParams = JSON.stringify(params); - } catch (error) { - const errorMsg = "'params' property must be a JSON formatted object"; - logger.error(errorMsg); - return reject(errorMsg); - } - } else { - stringParams = params; - } - - finalParams = stringParams; - - this.stream.session.openvidu.sendRequest( - 'execFilterMethod', - { streamId: this.stream.streamId, method, params: stringParams }, - (error, response) => { - if (error) { - logger.error('Error executing filter method for Stream ' + this.stream.streamId, error); - if (error.code === 401) { - return reject( - new OpenViduError( - OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, - "You don't have permissions to execute a filter method" - ) - ); - } else { - return reject(error); - } - } else { - return successExecMethod(true); - } - } - ); - } - }); - } - - /** - * Subscribe to certain filter event. Available events are specific for each filter - * - * @param eventType Event to which subscribe to. - * @param handler Function to execute upon event dispatched. It receives as parameter a {@link FilterEvent} object - * - * @returns A Promise (to which you can optionally subscribe to) that is resolved if the event listener was successfully attached to the filter and rejected with an Error object if not - */ - addEventListener(eventType: string, handler: (event: FilterEvent) => void): Promise { - return new Promise((resolve, reject) => { - logger.info('Adding filter event listener to event ' + eventType + ' to stream ' + this.stream.streamId); - this.stream.session.openvidu.sendRequest( - 'addFilterEventListener', - { streamId: this.stream.streamId, eventType }, - (error, response) => { - if (error) { - logger.error( - 'Error adding filter event listener to event ' + eventType + 'for Stream ' + this.stream.streamId, - error - ); - if (error.code === 401) { - return reject( - new OpenViduError( - OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, - "You don't have permissions to add a filter event listener" - ) - ); - } else { - return reject(error); - } - } else { - this.handlers.set(eventType, handler); - logger.info( - 'Filter event listener to event ' + eventType + ' successfully applied on Stream ' + this.stream.streamId - ); - return resolve(); - } - } - ); - }); - } - - /** - * Removes certain filter event listener previously set. - * - * @param eventType Event to unsubscribe from. - * - * @returns A Promise (to which you can optionally subscribe to) that is resolved if the event listener was successfully removed from the filter and rejected with an Error object in other case - */ - removeEventListener(eventType: string): Promise { - return new Promise((resolve, reject) => { - logger.info('Removing filter event listener to event ' + eventType + ' to stream ' + this.stream.streamId); - this.stream.session.openvidu.sendRequest( - 'removeFilterEventListener', - { streamId: this.stream.streamId, eventType }, - (error, response) => { - if (error) { - logger.error( - 'Error removing filter event listener to event ' + eventType + 'for Stream ' + this.stream.streamId, - error - ); - if (error.code === 401) { - return reject( - new OpenViduError( - OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, - "You don't have permissions to add a filter event listener" - ) - ); - } else { - return reject(error); - } - } else { - this.handlers.delete(eventType); - logger.info( - 'Filter event listener to event ' + eventType + ' successfully removed on Stream ' + this.stream.streamId - ); - return resolve(); - } - } - ); - }); - } -} diff --git a/openvidu-browser/src/OpenVidu/LocalRecorder.ts b/openvidu-browser/src/OpenVidu/LocalRecorder.ts deleted file mode 100644 index a66af732..00000000 --- a/openvidu-browser/src/OpenVidu/LocalRecorder.ts +++ /dev/null @@ -1,416 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Stream } from './Stream'; -import { LocalRecorderState } from '../OpenViduInternal/Enums/LocalRecorderState'; -import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; -import { PlatformUtils } from '../OpenViduInternal/Utils/Platform'; -import Mime = require('mime/lite'); - -/** - * @hidden - */ -const logger: OpenViduLogger = OpenViduLogger.getInstance(); - -/** - * @hidden - */ -let platform: PlatformUtils; - -/** - * Easy recording of {@link Stream} objects straightaway from the browser. Initialized with {@link OpenVidu.initLocalRecorder} method - */ -export class LocalRecorder { - state: LocalRecorderState; - - private connectionId: string; - private mediaRecorder: MediaRecorder; - private chunks: any[] = []; - private blob?: Blob; - private id: string; - private videoPreviewSrc: string; - private videoPreview: HTMLVideoElement; - - /** - * @hidden - */ - constructor(private stream: Stream) { - platform = PlatformUtils.getInstance(); - this.connectionId = !!this.stream.connection ? this.stream.connection.connectionId : 'default-connection'; - this.id = this.stream.streamId + '_' + this.connectionId + '_localrecord'; - this.state = LocalRecorderState.READY; - } - - /** - * Starts the recording of the Stream. {@link state} property must be `READY`. After method succeeds is set to `RECORDING` - * - * @param options The [MediaRecorder.options](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder#parameters) to be used to record this Stream. - * For example: - * - * ```javascript - * var OV = new OpenVidu(); - * var publisher = await OV.initPublisherAsync(); - * var localRecorder = OV.initLocalRecorder(publisher.stream); - * var options = { - * mimeType: 'video/webm;codecs=vp8', - * audioBitsPerSecond:128000, - * videoBitsPerSecond:2500000 - * }; - * localRecorder.record(options); - * ``` - * - * If not specified, the default options preferred by the platform will be used. - * - * @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording successfully started and rejected with an Error object if not - */ - record(options?: any): Promise { - return new Promise((resolve, reject) => { - try { - if (typeof options === 'string' || options instanceof String) { - return reject( - `When calling LocalRecorder.record(options) parameter 'options' cannot be a string. Must be an object like { mimeType: "${options}" }` - ); - } - if (typeof MediaRecorder === 'undefined') { - logger.error( - 'MediaRecorder not supported on your device. See compatibility in https://caniuse.com/#search=MediaRecorder' - ); - throw Error( - 'MediaRecorder not supported on your device. See compatibility in https://caniuse.com/#search=MediaRecorder' - ); - } - if (this.state !== LocalRecorderState.READY) { - throw Error( - "'LocalRecord.record()' needs 'LocalRecord.state' to be 'READY' (current value: '" + - this.state + - "'). Call 'LocalRecorder.clean()' or init a new LocalRecorder before" - ); - } - logger.log("Starting local recording of stream '" + this.stream.streamId + "' of connection '" + this.connectionId + "'"); - - if (!options) { - options = { mimeType: 'video/webm' }; - } else if (!options.mimeType) { - options.mimeType = 'video/webm'; - } - - this.mediaRecorder = new MediaRecorder(this.stream.getMediaStream(), options); - this.mediaRecorder.start(); - } catch (err) { - return reject(err); - } - - this.mediaRecorder.ondataavailable = (e) => { - if (e.data.size > 0) { - this.chunks.push(e.data); - } - }; - - this.mediaRecorder.onerror = (e) => { - logger.error('MediaRecorder error: ', e); - }; - - this.mediaRecorder.onstart = () => { - logger.log('MediaRecorder started (state=' + this.mediaRecorder.state + ')'); - }; - - this.mediaRecorder.onstop = () => { - this.onStopDefault(); - }; - - this.mediaRecorder.onpause = () => { - logger.log('MediaRecorder paused (state=' + this.mediaRecorder.state + ')'); - }; - - this.mediaRecorder.onresume = () => { - logger.log('MediaRecorder resumed (state=' + this.mediaRecorder.state + ')'); - }; - - this.state = LocalRecorderState.RECORDING; - return resolve(); - }); - } - - /** - * Ends the recording of the Stream. {@link state} property must be `RECORDING` or `PAUSED`. After method succeeds is set to `FINISHED` - * @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording successfully stopped and rejected with an Error object if not - */ - stop(): Promise { - return new Promise((resolve, reject) => { - try { - if (this.state === LocalRecorderState.READY || this.state === LocalRecorderState.FINISHED) { - throw Error( - "'LocalRecord.stop()' needs 'LocalRecord.state' to be 'RECORDING' or 'PAUSED' (current value: '" + - this.state + - "'). Call 'LocalRecorder.start()' before" - ); - } - this.mediaRecorder.onstop = () => { - this.onStopDefault(); - return resolve(); - }; - this.mediaRecorder.stop(); - } catch (e) { - return reject(e); - } - }); - } - - /** - * Pauses the recording of the Stream. {@link state} property must be `RECORDING`. After method succeeds is set to `PAUSED` - * @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording was successfully paused and rejected with an Error object if not - */ - pause(): Promise { - return new Promise((resolve, reject) => { - try { - if (this.state !== LocalRecorderState.RECORDING) { - return reject( - Error( - "'LocalRecord.pause()' needs 'LocalRecord.state' to be 'RECORDING' (current value: '" + - this.state + - "'). Call 'LocalRecorder.start()' or 'LocalRecorder.resume()' before" - ) - ); - } - this.mediaRecorder.pause(); - this.state = LocalRecorderState.PAUSED; - return resolve(); - } catch (error) { - return reject(error); - } - }); - } - - /** - * Resumes the recording of the Stream. {@link state} property must be `PAUSED`. After method succeeds is set to `RECORDING` - * @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording was successfully resumed and rejected with an Error object if not - */ - resume(): Promise { - return new Promise((resolve, reject) => { - try { - if (this.state !== LocalRecorderState.PAUSED) { - throw Error( - "'LocalRecord.resume()' needs 'LocalRecord.state' to be 'PAUSED' (current value: '" + - this.state + - "'). Call 'LocalRecorder.pause()' before" - ); - } - this.mediaRecorder.resume(); - this.state = LocalRecorderState.RECORDING; - return resolve(); - } catch (error) { - return reject(error); - } - }); - } - - /** - * Previews the recording, appending a new HTMLVideoElement to element with id `parentId`. {@link state} property must be `FINISHED` - */ - preview(parentElement): HTMLVideoElement { - if (this.state !== LocalRecorderState.FINISHED) { - throw Error( - "'LocalRecord.preview()' needs 'LocalRecord.state' to be 'FINISHED' (current value: '" + - this.state + - "'). Call 'LocalRecorder.stop()' before" - ); - } - - this.videoPreview = document.createElement('video'); - - this.videoPreview.id = this.id; - this.videoPreview.autoplay = true; - - if (platform.isSafariBrowser()) { - this.videoPreview.playsInline = true; - } - - if (typeof parentElement === 'string') { - const parentElementDom = document.getElementById(parentElement); - if (parentElementDom) { - this.videoPreview = parentElementDom.appendChild(this.videoPreview); - } - } else { - this.videoPreview = parentElement.appendChild(this.videoPreview); - } - - this.videoPreview.src = this.videoPreviewSrc; - - return this.videoPreview; - } - - /** - * Gracefully stops and cleans the current recording (WARNING: it is completely dismissed). Sets {@link state} to `READY` so the recording can start again - */ - clean(): void { - const f = () => { - delete this.blob; - this.chunks = []; - this.state = LocalRecorderState.READY; - }; - if (this.state === LocalRecorderState.RECORDING || this.state === LocalRecorderState.PAUSED) { - this.stop() - .then(() => f()) - .catch(() => f()); - } else { - f(); - } - } - - /** - * Downloads the recorded video through the browser. {@link state} property must be `FINISHED` - */ - download(): void { - if (this.state !== LocalRecorderState.FINISHED) { - throw Error( - "'LocalRecord.download()' needs 'LocalRecord.state' to be 'FINISHED' (current value: '" + - this.state + - "'). Call 'LocalRecorder.stop()' before" - ); - } else { - const a: HTMLAnchorElement = document.createElement('a'); - a.style.display = 'none'; - document.body.appendChild(a); - - const url = globalThis.URL.createObjectURL(this.blob); - a.href = url; - a.download = this.id + '.' + Mime.getExtension(this.blob!.type); - a.click(); - globalThis.URL.revokeObjectURL(url); - - document.body.removeChild(a); - } - } - - /** - * Gets the raw Blob file. Methods preview, download, uploadAsBinary and uploadAsMultipartfile use this same file to perform their specific actions. {@link state} property must be `FINISHED` - */ - getBlob(): Blob { - if (this.state !== LocalRecorderState.FINISHED) { - throw Error("Call 'LocalRecord.stop()' before getting Blob file"); - } else { - return this.blob!; - } - } - - /** - * Uploads the recorded video as a binary file performing an HTTP/POST operation to URL `endpoint`. {@link state} property must be `FINISHED`. Optional HTTP headers can be passed as second parameter. For example: - * ``` - * var headers = { - * "Cookie": "$Version=1; Skin=new;", - * "Authorization":"Basic QWxhZGpbjpuIHNlctZQ==" - * } - * ``` - * @returns A Promise (to which you can optionally subscribe to) that is resolved with the `http.responseText` from server if the operation was successful and rejected with the failed `http.status` if not - */ - uploadAsBinary(endpoint: string, headers?: any): Promise { - return new Promise((resolve, reject) => { - if (this.state !== LocalRecorderState.FINISHED) { - return reject( - Error( - "'LocalRecord.uploadAsBinary()' needs 'LocalRecord.state' to be 'FINISHED' (current value: '" + - this.state + - "'). Call 'LocalRecorder.stop()' before" - ) - ); - } else { - const http = new XMLHttpRequest(); - http.open('POST', endpoint, true); - - if (typeof headers === 'object') { - for (const key of Object.keys(headers)) { - http.setRequestHeader(key, headers[key]); - } - } - - http.onreadystatechange = () => { - if (http.readyState === 4) { - if (http.status.toString().charAt(0) === '2') { - // Success response from server (HTTP status standard: 2XX is success) - return resolve(http.responseText); - } else { - return reject(http.status); - } - } - }; - http.send(this.blob); - } - }); - } - - /** - * Uploads the recorded video as a multipart file performing an HTTP/POST operation to URL `endpoint`. {@link state} property must be `FINISHED`. Optional HTTP headers can be passed as second parameter. For example: - * ``` - * var headers = { - * "Cookie": "$Version=1; Skin=new;", - * "Authorization":"Basic QWxhZGpbjpuIHNlctZQ==" - * } - * ``` - * @returns A Promise (to which you can optionally subscribe to) that is resolved with the `http.responseText` from server if the operation was successful and rejected with the failed `http.status` if not: - */ - uploadAsMultipartfile(endpoint: string, headers?: any): Promise { - return new Promise((resolve, reject) => { - if (this.state !== LocalRecorderState.FINISHED) { - return reject( - Error( - "'LocalRecord.uploadAsMultipartfile()' needs 'LocalRecord.state' to be 'FINISHED' (current value: '" + - this.state + - "'). Call 'LocalRecorder.stop()' before" - ) - ); - } else { - const http = new XMLHttpRequest(); - http.open('POST', endpoint, true); - - if (typeof headers === 'object') { - for (const key of Object.keys(headers)) { - http.setRequestHeader(key, headers[key]); - } - } - - const sendable = new FormData(); - sendable.append('file', this.blob!, this.id + '.' + Mime.getExtension(this.blob!.type)); - - http.onreadystatechange = () => { - if (http.readyState === 4) { - if (http.status.toString().charAt(0) === '2') { - // Success response from server (HTTP status standard: 2XX is success) - return resolve(http.responseText); - } else { - return reject(http.status); - } - } - }; - - http.send(sendable); - } - }); - } - - /* Private methods */ - - private onStopDefault(): void { - logger.log('MediaRecorder stopped (state=' + this.mediaRecorder.state + ')'); - - this.blob = new Blob(this.chunks, { type: this.mediaRecorder.mimeType }); - this.chunks = []; - - this.videoPreviewSrc = globalThis.URL.createObjectURL(this.blob); - - this.state = LocalRecorderState.FINISHED; - } -} diff --git a/openvidu-browser/src/OpenVidu/OpenVidu.ts b/openvidu-browser/src/OpenVidu/OpenVidu.ts deleted file mode 100644 index fa7f6191..00000000 --- a/openvidu-browser/src/OpenVidu/OpenVidu.ts +++ /dev/null @@ -1,1301 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { LocalRecorder } from './LocalRecorder'; -import { Publisher } from './Publisher'; -import { Session } from './Session'; -import { Stream } from './Stream'; -import { SessionDisconnectedEvent } from '../OpenViduInternal/Events/SessionDisconnectedEvent'; -import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent'; -import { Device } from '../OpenViduInternal/Interfaces/Public/Device'; -import { OpenViduAdvancedConfiguration } from '../OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration'; -import { PublisherProperties } from '../OpenViduInternal/Interfaces/Public/PublisherProperties'; -import { CustomMediaStreamConstraints } from '../OpenViduInternal/Interfaces/Private/CustomMediaStreamConstraints'; -import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError'; -import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode'; -import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; -import { PlatformUtils } from '../OpenViduInternal/Utils/Platform'; -import { StreamPropertyChangedEventReason, ChangedPropertyType } from '../OpenViduInternal/Events/Types/Types'; - -import * as screenSharingAuto from '../OpenViduInternal/ScreenSharing/Screen-Capturing-Auto'; -import * as screenSharing from '../OpenViduInternal/ScreenSharing/Screen-Capturing'; -import { OpenViduLoggerConfiguration } from '../OpenViduInternal/Logger/OpenViduLoggerConfiguration'; -/** - * @hidden - */ -import EventEmitter = require('wolfy87-eventemitter'); -/** - * @hidden - */ -import RpcBuilder = require('../OpenViduInternal/KurentoUtils/kurento-jsonrpc'); - -/** - * @hidden - */ -const packageJson = require('../../package.json'); -/** - * @hidden - */ -declare var cordova: any; -/** - * @hidden - */ -const logger: OpenViduLogger = OpenViduLogger.getInstance(); - -/** - * @hidden - */ -let platform: PlatformUtils; - -/** - * Entrypoint of OpenVidu Browser library. - * Use it to initialize objects of type {@link Session}, {@link Publisher} and {@link LocalRecorder} - */ -export class OpenVidu { - private jsonRpcClient: any; - private masterNodeHasCrashed = false; - - /** - * @hidden - */ - session: Session; - /** - * @hidden - */ - publishers: Publisher[] = []; - /** - * @hidden - */ - wsUri: string; - /** - * @hidden - */ - httpUri: string; - /** - * @hidden - */ - secret = ''; - /** - * @hidden - */ - recorder = false; - /** - * @hidden - */ - stt = false; - /** - * @hidden - */ - iceServers: RTCIceServer[]; - /** - * @hidden - */ - role: string; - /** - * @hidden - */ - finalUserId: string; - /** - * @hidden - */ - mediaServer: string; - /** - * @hidden - */ - videoSimulcast: boolean; - /** - * @hidden - */ - life: number = -1; - /** - * @hidden - */ - advancedConfiguration: OpenViduAdvancedConfiguration = {}; - /** - * @hidden - */ - webrtcStatsInterval: number = -1; - /** - * @hidden - */ - sendBrowserLogs: OpenViduLoggerConfiguration = OpenViduLoggerConfiguration.disabled; - /** - * @hidden - */ - isAtLeastPro: boolean = false; - /** - * @hidden - */ - isEnterprise: boolean = false; - /** - * @hidden - */ - libraryVersion: string; - /** - * @hidden - */ - ee = new EventEmitter(); - - constructor() { - platform = PlatformUtils.getInstance(); - this.libraryVersion = packageJson.version; - logger.info('OpenVidu initialized'); - logger.info('Platform detected: ' + platform.getDescription()); - logger.info('openvidu-browser version: ' + this.libraryVersion); - - if (platform.isMobileDevice() || platform.isReactNative()) { - // Listen to orientationchange only on mobile devices - this.onOrientationChanged(() => { - this.publishers.forEach((publisher) => { - if (publisher.stream.isLocalStreamPublished && !!publisher.stream && !!publisher.stream.hasVideo) { - this.sendNewVideoDimensionsIfRequired(publisher, 'deviceRotated', 75, 10); - } - }); - }); - } - } - - /** - * Returns new session - */ - initSession(): Session { - this.session = new Session(this); - return this.session; - } - - initPublisher(targetElement: string | HTMLElement | undefined): Publisher; - initPublisher(targetElement: string | HTMLElement | undefined, properties: PublisherProperties): Publisher; - initPublisher(targetElement: string | HTMLElement | undefined, completionHandler: (error: Error | undefined) => void): Publisher; - initPublisher( - targetElement: string | HTMLElement | undefined, - properties: PublisherProperties, - completionHandler: (error: Error | undefined) => void - ): Publisher; - - /** - * Returns a new publisher - * - * #### Events dispatched - * - * The {@link Publisher} object will dispatch an `accessDialogOpened` event, only if the pop-up shown by the browser to request permissions for the camera is opened. You can use this event to alert the user about granting permissions - * for your website. An `accessDialogClosed` event will also be dispatched after user clicks on "Allow" or "Block" in the pop-up. - * - * The {@link Publisher} object will dispatch an `accessAllowed` or `accessDenied` event once it has been granted access to the requested input devices or not. - * - * The {@link Publisher} object will dispatch a `videoElementCreated` event once a HTML video element has been added to DOM (only if you - * [let OpenVidu take care of the video players](/en/stable/cheatsheet/manage-videos/#let-openvidu-take-care-of-the-video-players)). See {@link VideoElementEvent} to learn more. - * - * The {@link Publisher} object will dispatch a `streamPlaying` event once the local streams starts playing. See {@link StreamManagerEvent} to learn more. - * - * @param targetElement HTML DOM element (or its `id` attribute) in which the video element of the Publisher will be inserted (see {@link PublisherProperties.insertMode}). If *null* or *undefined* no default video will be created for this Publisher. - * You can always call method {@link Publisher.addVideoElement} or {@link Publisher.createVideoElement} to manage the video elements on your own (see [Manage video players](/en/stable/cheatsheet/manage-videos) section) - * @param completionHandler `error` parameter is null if `initPublisher` succeeds, and is defined if it fails. - * `completionHandler` function is called before the Publisher dispatches an `accessAllowed` or an `accessDenied` event - */ - initPublisher(targetElement: string | HTMLElement | undefined, param2?, param3?): Publisher { - let properties: PublisherProperties; - - if (!!param2 && typeof param2 !== 'function') { - // Matches 'initPublisher(targetElement, properties)' or 'initPublisher(targetElement, properties, completionHandler)' - - properties = param2; - - properties = { - audioSource: typeof properties.audioSource !== 'undefined' ? properties.audioSource : undefined, - frameRate: - typeof MediaStreamTrack !== 'undefined' && properties.videoSource instanceof MediaStreamTrack - ? undefined - : typeof properties.frameRate !== 'undefined' - ? properties.frameRate - : undefined, - insertMode: - typeof properties.insertMode !== 'undefined' - ? typeof properties.insertMode === 'string' - ? VideoInsertMode[properties.insertMode] - : properties.insertMode - : VideoInsertMode.APPEND, - mirror: typeof properties.mirror !== 'undefined' ? properties.mirror : true, - publishAudio: typeof properties.publishAudio !== 'undefined' ? properties.publishAudio : true, - publishVideo: typeof properties.publishVideo !== 'undefined' ? properties.publishVideo : true, - resolution: - typeof MediaStreamTrack !== 'undefined' && properties.videoSource instanceof MediaStreamTrack - ? undefined - : typeof properties.resolution !== 'undefined' - ? properties.resolution - : '640x480', - videoSource: typeof properties.videoSource !== 'undefined' ? properties.videoSource : undefined, - videoSimulcast: properties.videoSimulcast, - filter: properties.filter - }; - } else { - // Matches 'initPublisher(targetElement)' or 'initPublisher(targetElement, completionHandler)' - - properties = { - insertMode: VideoInsertMode.APPEND, - mirror: true, - publishAudio: true, - publishVideo: true, - resolution: '640x480' - }; - } - - const publisher: Publisher = new Publisher(targetElement, properties, this); - - let completionHandler: (error: Error | undefined) => void; - if (!!param2 && typeof param2 === 'function') { - completionHandler = param2; - } else if (!!param3) { - completionHandler = param3; - } - - publisher - .initialize() - .then(() => { - if (completionHandler !== undefined) { - completionHandler(undefined); - } - publisher.emitEvent('accessAllowed', []); - }) - .catch((error) => { - if (completionHandler !== undefined) { - completionHandler(error); - } - publisher.emitEvent('accessDenied', [error]); - }); - - this.publishers.push(publisher); - return publisher; - } - - /** - * Promisified version of {@link OpenVidu.initPublisher} - * - * > WARNING: events `accessDialogOpened` and `accessDialogClosed` will not be dispatched if using this method instead of {@link OpenVidu.initPublisher} - */ - initPublisherAsync(targetElement: string | HTMLElement | undefined): Promise; - initPublisherAsync(targetElement: string | HTMLElement | undefined, properties: PublisherProperties): Promise; - - initPublisherAsync(targetElement: string | HTMLElement | undefined, properties?: PublisherProperties): Promise { - return new Promise((resolve, reject) => { - let publisher: Publisher; - - const callback = (error: Error) => { - if (!!error) { - return reject(error); - } else { - return resolve(publisher); - } - }; - - if (!!properties) { - publisher = this.initPublisher(targetElement, properties, callback); - } else { - publisher = this.initPublisher(targetElement, callback); - } - }); - } - - /** - * Returns a new local recorder for recording streams straight away from the browser - * @param stream Stream to record - */ - initLocalRecorder(stream: Stream): LocalRecorder { - return new LocalRecorder(stream); - } - - /** - * Checks if the browser supports OpenVidu - * @returns 1 if the browser supports OpenVidu, 0 otherwise - */ - checkSystemRequirements(): boolean { - // Specific iOS platform support (iPhone, iPad) - if (platform.isIPhoneOrIPad()) { - return ( - platform.isIOSWithSafari() || - platform.isChromeMobileBrowser() || - platform.isFirefoxMobileBrowser() || - platform.isOperaMobileBrowser() || - platform.isEdgeMobileBrowser() || - platform.isIonicIos() // Ionic apps for iOS - ); - } - - // General platform support for web clients (Desktop, Mobile) - return ( - platform.isChromeBrowser() || - platform.isChromeMobileBrowser() || - platform.isFirefoxBrowser() || - platform.isFirefoxMobileBrowser() || - platform.isOperaBrowser() || - platform.isOperaMobileBrowser() || - platform.isEdgeBrowser() || - platform.isEdgeMobileBrowser() || - platform.isSamsungBrowser() || - platform.isSafariBrowser() || - platform.isAndroidBrowser() || // Android WebView & Ionic apps for Android - platform.isElectron() || - platform.isNodeJs() || - // TODO: remove when updating platform detection library - platform.isMotorolaEdgeDevice() - ); - } - - /** - * Checks if the browser supports screen-sharing. Desktop Chrome, Firefox and Opera support screen-sharing - * @returns 1 if the browser supports screen-sharing, 0 otherwise - */ - checkScreenSharingCapabilities(): boolean { - return platform.canScreenShare(); - } - - /** - * Collects information about the media input devices available on the system. You can pass property `deviceId` of a {@link Device} object as value of `audioSource` or `videoSource` properties in {@link initPublisher} method - */ - getDevices(): Promise { - return new Promise((resolve, reject) => { - navigator.mediaDevices - .enumerateDevices() - .then((deviceInfos) => { - const devices: Device[] = []; - - // Ionic Android devices - if (platform.isIonicAndroid() && typeof cordova != 'undefined' && cordova?.plugins?.EnumerateDevicesPlugin) { - cordova.plugins.EnumerateDevicesPlugin.getEnumerateDevices().then((pluginDevices: Device[]) => { - let pluginAudioDevices: Device[] = []; - let videoDevices: Device[] = []; - let audioDevices: Device[] = []; - pluginAudioDevices = pluginDevices.filter((device: Device) => device.kind === 'audioinput'); - videoDevices = deviceInfos.filter((device: MediaDeviceInfo) => device.kind === 'videoinput') as any; - audioDevices = deviceInfos.filter((device: MediaDeviceInfo) => device.kind === 'audioinput') as any; - videoDevices.forEach((deviceInfo, index) => { - if (!deviceInfo.label) { - let label = ''; - if (index === 0) { - label = 'Front Camera'; - } else if (index === 1) { - label = 'Back Camera'; - } else { - label = 'Unknown Camera'; - } - devices.push({ - kind: deviceInfo.kind, - deviceId: deviceInfo.deviceId, - label: label - }); - } else { - devices.push({ - kind: deviceInfo.kind, - deviceId: deviceInfo.deviceId, - label: deviceInfo.label - }); - } - }); - audioDevices.forEach((deviceInfo, index) => { - if (!deviceInfo.label) { - let label = ''; - switch (index) { - case 0: // Default Microphone - label = 'Default'; - break; - case 1: // Microphone + Speakerphone - const defaultMatch = pluginAudioDevices.filter((d) => d.label.includes('Built'))[0]; - label = defaultMatch ? defaultMatch.label : 'Built-in Microphone'; - break; - case 2: // Headset Microphone - const wiredMatch = pluginAudioDevices.filter((d) => d.label.includes('Wired'))[0]; - if (wiredMatch) { - label = wiredMatch.label; - } else { - label = 'Headset earpiece'; - } - break; - case 3: - const wirelessMatch = pluginAudioDevices.filter((d) => d.label.includes('Bluetooth'))[0]; - label = wirelessMatch ? wirelessMatch.label : 'Wireless'; - break; - default: - label = 'Unknown Microphone'; - break; - } - devices.push({ - kind: deviceInfo.kind, - deviceId: deviceInfo.deviceId, - label: label - }); - } else { - devices.push({ - kind: deviceInfo.kind, - deviceId: deviceInfo.deviceId, - label: deviceInfo.label - }); - } - }); - return resolve(devices); - }); - } else { - // Rest of platforms - deviceInfos.forEach((deviceInfo) => { - if (deviceInfo.kind === 'audioinput' || deviceInfo.kind === 'videoinput') { - devices.push({ - kind: deviceInfo.kind, - deviceId: deviceInfo.deviceId, - label: deviceInfo.label - }); - } - }); - return resolve(devices); - } - }) - .catch((error) => { - logger.error('Error getting devices', error); - return reject(error); - }); - }); - } - - /** - * Get a MediaStream object that you can customize before calling {@link initPublisher} (pass _MediaStreamTrack_ property of the _MediaStream_ value resolved by the Promise as `audioSource` or `videoSource` properties in {@link initPublisher}) - * - * Parameter `options` is the same as in {@link initPublisher} second parameter (of type {@link PublisherProperties}), but only the following properties will be applied: `audioSource`, `videoSource`, `frameRate`, `resolution` - * - * To customize the Publisher's video, the API for HTMLCanvasElement is very useful. For example, to get a black-and-white video at 10 fps and HD resolution with no sound: - * ``` - * var OV = new OpenVidu(); - * var FRAME_RATE = 10; - * - * OV.getUserMedia({ - * audioSource: false, - * videoSource: undefined, - * resolution: '1280x720', - * frameRate: FRAME_RATE - * }) - * .then(mediaStream => { - * - * var videoTrack = mediaStream.getVideoTracks()[0]; - * var video = document.createElement('video'); - * video.srcObject = new MediaStream([videoTrack]); - * - * var canvas = document.createElement('canvas'); - * var ctx = canvas.getContext('2d'); - * ctx.filter = 'grayscale(100%)'; - * - * video.addEventListener('play', () => { - * var loop = () => { - * if (!video.paused && !video.ended) { - * ctx.drawImage(video, 0, 0, 300, 170); - * setTimeout(loop, 1000/ FRAME_RATE); // Drawing at 10 fps - * } - * }; - * loop(); - * }); - * video.play(); - * - * var grayVideoTrack = canvas.captureStream(FRAME_RATE).getVideoTracks()[0]; - * var publisher = this.OV.initPublisher( - * myHtmlTarget, - * { - * audioSource: false, - * videoSource: grayVideoTrack - * }); - * }); - * ``` - */ - async getUserMedia(options: PublisherProperties): Promise { - const askForAudioStreamOnly = async (previousMediaStream: MediaStream, constraints: MediaStreamConstraints) => { - const definedAudioConstraint = constraints.audio === undefined ? true : constraints.audio; - const constraintsAux: MediaStreamConstraints = { audio: definedAudioConstraint, video: false }; - try { - const audioOnlyStream = await navigator.mediaDevices.getUserMedia(constraintsAux); - previousMediaStream.addTrack(audioOnlyStream.getAudioTracks()[0]); - return previousMediaStream; - } catch (error) { - previousMediaStream.getAudioTracks().forEach((track) => { - track.stop(); - }); - previousMediaStream.getVideoTracks().forEach((track) => { - track.stop(); - }); - throw this.generateAudioDeviceError(error, constraintsAux); - } - }; - - try { - const myConstraints = await this.generateMediaConstraints(options); - if ( - (!!myConstraints.videoTrack && !!myConstraints.audioTrack) || - (!!myConstraints.audioTrack && myConstraints.constraints?.video === false) || - (!!myConstraints.videoTrack && myConstraints.constraints?.audio === false) - ) { - // No need to call getUserMedia at all. Both tracks provided, or only AUDIO track provided or only VIDEO track provided - return this.addAlreadyProvidedTracks(myConstraints, new MediaStream()); - } else { - // getUserMedia must be called. AUDIO or VIDEO are requesting a new track - - // Delete already provided constraints for audio or video - if (!!myConstraints.videoTrack) { - delete myConstraints.constraints!.video; - } - if (!!myConstraints.audioTrack) { - delete myConstraints.constraints!.audio; - } - - let mustAskForAudioTrackLater = false; - if (typeof options.videoSource === 'string') { - // Video is deviceId or screen sharing - if ( - options.videoSource === 'screen' || - options.videoSource === 'window' || - (platform.isElectron() && options.videoSource.startsWith('screen:')) - ) { - // Video is screen sharing - mustAskForAudioTrackLater = - !myConstraints.audioTrack && options.audioSource !== null && options.audioSource !== false; - if (navigator.mediaDevices['getDisplayMedia'] && !platform.isElectron()) { - // getDisplayMedia supported - try { - const mediaStream = await navigator.mediaDevices['getDisplayMedia']({ video: true, audio: options.audioSource === 'screen' }); - this.addAlreadyProvidedTracks(myConstraints, mediaStream); - if (mustAskForAudioTrackLater) { - return await askForAudioStreamOnly(mediaStream, myConstraints.constraints); - } else { - return mediaStream; - } - } catch (error) { - let errorName: OpenViduErrorName = OpenViduErrorName.SCREEN_CAPTURE_DENIED; - const errorMessage = error.toString(); - throw new OpenViduError(errorName, errorMessage); - } - } else { - // getDisplayMedia NOT supported. Can perform getUserMedia below with already calculated constraints - } - } else { - // Video is deviceId. Can perform getUserMedia below with already calculated constraints - } - } - // Use already calculated constraints - const constraintsAux = mustAskForAudioTrackLater - ? { video: myConstraints.constraints!.video } - : myConstraints.constraints; - try { - const mediaStream = await navigator.mediaDevices.getUserMedia(constraintsAux); - this.addAlreadyProvidedTracks(myConstraints, mediaStream); - if (mustAskForAudioTrackLater) { - return await askForAudioStreamOnly(mediaStream, myConstraints.constraints); - } else { - return mediaStream; - } - } catch (error) { - let errorName: OpenViduErrorName; - const errorMessage = error.toString(); - if (!(options.videoSource === 'screen')) { - errorName = OpenViduErrorName.DEVICE_ACCESS_DENIED; - } else { - errorName = OpenViduErrorName.SCREEN_CAPTURE_DENIED; - } - throw new OpenViduError(errorName, errorMessage); - } - } - } catch (error) { - throw error; - } - } - - /* tslint:disable:no-empty */ - /** - * Disable all logging except error level - */ - enableProdMode(): void { - logger.enableProdMode(); - } - /* tslint:enable:no-empty */ - - /** - * Set OpenVidu advanced configuration options. `configuration` is an object of type {@link OpenViduAdvancedConfiguration}. Call this method to override previous values at any moment. - */ - setAdvancedConfiguration(configuration: OpenViduAdvancedConfiguration): void { - this.advancedConfiguration = configuration; - } - - /* Hidden methods */ - - /** - * @hidden - */ - onOrientationChanged(handler): void { - (globalThis as any).addEventListener('orientationchange', handler); - } - - /** - * @hidden - */ - sendNewVideoDimensionsIfRequired(publisher: Publisher, reason: StreamPropertyChangedEventReason, WAIT_INTERVAL: number, MAX_ATTEMPTS: number) { - let attempts = 0; - const oldWidth = publisher?.stream?.videoDimensions?.width || 0; - const oldHeight = publisher?.stream?.videoDimensions?.height || 0; - - const repeatUntilChangeOrMaxAttempts: NodeJS.Timeout = setInterval(() => { - attempts++; - if (attempts > MAX_ATTEMPTS) { - clearTimeout(repeatUntilChangeOrMaxAttempts); - } - publisher.getVideoDimensions().then((newDimensions) => { - if (newDimensions.width !== oldWidth || newDimensions.height !== oldHeight) { - clearTimeout(repeatUntilChangeOrMaxAttempts); - this.sendVideoDimensionsChangedEvent(publisher, reason, oldWidth, oldHeight, newDimensions.width, newDimensions.height); - } - }); - }, WAIT_INTERVAL); - } - - /** - * @hidden - */ - sendVideoDimensionsChangedEvent( - publisher: Publisher, - reason: StreamPropertyChangedEventReason, - oldWidth: number, - oldHeight: number, - newWidth: number, - newHeight: number - ) { - publisher.stream.videoDimensions = { - width: newWidth || 0, - height: newHeight || 0 - }; - this.sendRequest( - 'streamPropertyChanged', - { - streamId: publisher.stream.streamId, - property: 'videoDimensions', - newValue: JSON.stringify(publisher.stream.videoDimensions), - reason - }, - (error, response) => { - if (error) { - logger.error("Error sending 'streamPropertyChanged' event", error); - } else { - this.session.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent( - this.session, - publisher.stream, - 'videoDimensions', - publisher.stream.videoDimensions, - { width: oldWidth, height: oldHeight }, - reason - ) - ]); - publisher.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent( - publisher, - publisher.stream, - 'videoDimensions', - publisher.stream.videoDimensions, - { width: oldWidth, height: oldHeight }, - reason - ) - ]); - this.session.sendVideoData(publisher); - } - } - ); - } - - /** - * @hidden - */ - sendTrackChangedEvent(publisher: Publisher, oldLabel: string, newLabel: string, propertyType: ChangedPropertyType) { - const oldValue = { label: oldLabel }; - const newValue = { label: newLabel }; - const reason = 'trackReplaced'; - - if (publisher.stream.isLocalStreamPublished) { - this.sendRequest( - 'streamPropertyChanged', - { - streamId: publisher.stream.streamId, - property: propertyType, - newValue: newValue, - reason - }, - (error, response) => { - if (error) { - logger.error("Error sending 'streamPropertyChanged' event", error); - } else { - this.session.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent(this.session, publisher.stream, propertyType, newValue, oldValue, reason) - ]); - publisher.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent(publisher, publisher.stream, propertyType, newValue, oldValue, reason) - ]); - } - } - ); - } else { - this.session.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent(this.session, publisher.stream, propertyType, newValue, oldValue, reason) - ]); - publisher.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent(publisher, publisher.stream, propertyType, newValue, oldValue, reason) - ]); - } - } - - /** - * @hidden - */ - generateMediaConstraints(publisherProperties: PublisherProperties): Promise { - return new Promise((resolve, reject) => { - const myConstraints: CustomMediaStreamConstraints = { - audioTrack: undefined, - videoTrack: undefined, - constraints: { - audio: undefined, - video: undefined - } - }; - const audioSource = publisherProperties.audioSource; - const videoSource = publisherProperties.videoSource; - - // CASE 1: null/false - if (audioSource === null || audioSource === false) { - // No audio track - myConstraints.constraints!.audio = false; - } - if (videoSource === null || videoSource === false) { - // No video track - myConstraints.constraints!.video = false; - } - if (myConstraints.constraints!.audio === false && myConstraints.constraints!.video === false) { - // ERROR! audioSource and videoSource cannot be both false at the same time - return reject( - new OpenViduError( - OpenViduErrorName.NO_INPUT_SOURCE_SET, - "Properties 'audioSource' and 'videoSource' cannot be set to false or null at the same time" - ) - ); - } - - // CASE 2: MediaStreamTracks - if (typeof MediaStreamTrack !== 'undefined' && audioSource instanceof MediaStreamTrack) { - // Already provided audio track - myConstraints.audioTrack = audioSource; - } - if (typeof MediaStreamTrack !== 'undefined' && videoSource instanceof MediaStreamTrack) { - // Already provided video track - myConstraints.videoTrack = videoSource; - } - - // CASE 3: Default tracks - if (audioSource === undefined) { - myConstraints.constraints!.audio = true; - } - if (videoSource === undefined) { - myConstraints.constraints!.video = { - width: { - ideal: 640 - }, - height: { - ideal: 480 - } - }; - } - - // CASE 3.5: give values to resolution and frameRate if video not null/false - if (videoSource !== null && videoSource !== false) { - if (!!publisherProperties.resolution) { - const widthAndHeight = publisherProperties.resolution.toLowerCase().split('x'); - const idealWidth = Number(widthAndHeight[0]); - const idealHeight = Number(widthAndHeight[1]); - myConstraints.constraints!.video = { - width: { - ideal: idealWidth - }, - height: { - ideal: idealHeight - } - }; - } - if (!!publisherProperties.frameRate) { - (myConstraints.constraints!.video).frameRate = { ideal: publisherProperties.frameRate }; - } - } - - // CASE 4: deviceId or screen sharing - this.configureDeviceIdOrScreensharing(myConstraints, publisherProperties, resolve, reject); - - return resolve(myConstraints); - }); - } - - /** - * @hidden - */ - startWs(onConnectSucces: (error: Error) => void): void { - const config = { - heartbeat: 5000, - ws: { - uri: this.wsUri + '?sessionId=' + this.session.sessionId, - onconnected: onConnectSucces, - ondisconnect: this.disconnectCallback.bind(this), - onreconnecting: this.reconnectingCallback.bind(this), - onreconnected: this.reconnectedCallback.bind(this), - ismasternodecrashed: this.isMasterNodeCrashed.bind(this) - }, - rpc: { - requestTimeout: 10000, - heartbeatRequestTimeout: 5000, - participantJoined: this.session.onParticipantJoined.bind(this.session), - participantPublished: this.session.onParticipantPublished.bind(this.session), - participantUnpublished: this.session.onParticipantUnpublished.bind(this.session), - participantLeft: this.session.onParticipantLeft.bind(this.session), - participantEvicted: this.session.onParticipantEvicted.bind(this.session), - recordingStarted: this.session.onRecordingStarted.bind(this.session), - recordingStopped: this.session.onRecordingStopped.bind(this.session), - broadcastStarted: this.session.onBroadcastStarted.bind(this.session), - broadcastStopped: this.session.onBroadcastStopped.bind(this.session), - sendMessage: this.session.onNewMessage.bind(this.session), - streamPropertyChanged: this.session.onStreamPropertyChanged.bind(this.session), - connectionPropertyChanged: this.session.onConnectionPropertyChanged.bind(this.session), - networkQualityLevelChanged: this.session.onNetworkQualityLevelChangedChanged.bind(this.session), - filterEventDispatched: this.session.onFilterEventDispatched.bind(this.session), - iceCandidate: this.session.recvIceCandidate.bind(this.session), - mediaError: this.session.onMediaError.bind(this.session), - masterNodeCrashedNotification: this.onMasterNodeCrashedNotification.bind(this), - forciblyReconnectSubscriber: this.session.onForciblyReconnectSubscriber.bind(this.session), - speechToTextMessage: this.session.onSpeechToTextMessage.bind(this.session), - speechToTextDisconnected: this.session.onSpeechToTextDisconnected.bind(this.session) - } - }; - this.jsonRpcClient = new RpcBuilder.clients.JsonRpcClient(config); - } - - /** - * @hidden - */ - onMasterNodeCrashedNotification(response): void { - console.error('Master Node has crashed'); - this.masterNodeHasCrashed = true; - this.session.onLostConnection('nodeCrashed'); - this.jsonRpcClient.close(4103, 'Master Node has crashed'); - } - - /** - * @hidden - */ - getWsReadyState(): number { - return this.jsonRpcClient.getReadyState(); - } - - /** - * @hidden - */ - closeWs(): void { - this.jsonRpcClient.close(4102, 'Connection closed by client'); - } - - /** - * @hidden - */ - sendRequest(method: string, params: any, callback?): void { - if (params && params instanceof Function) { - callback = params; - params = {}; - } - logger.debug('Sending request: {method:"' + method + '", params: ' + JSON.stringify(params) + '}'); - this.jsonRpcClient?.send(method, params, callback); - } - - /** - * @hidden - */ - getWsUri(): string { - return this.wsUri; - } - - /** - * @hidden - */ - getSecret(): string { - return this.secret; - } - - /** - * @hidden - */ - getRecorder(): boolean { - return this.recorder; - } - - /** - * @hidden - */ - getStt(): boolean { - return this.stt; - } - - /** - * @hidden - */ - generateAudioDeviceError(error, constraints: MediaStreamConstraints): OpenViduError { - if (error.name === 'Error') { - // Safari OverConstrainedError has as name property 'Error' instead of 'OverConstrainedError' - error.name = error.constructor.name; - } - let errorName, errorMessage: string; - switch (error.name.toLowerCase()) { - case 'notfounderror': - errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND; - errorMessage = error.toString(); - return new OpenViduError(errorName, errorMessage); - case 'notallowederror': - errorName = OpenViduErrorName.DEVICE_ACCESS_DENIED; - errorMessage = error.toString(); - return new OpenViduError(errorName, errorMessage); - case 'overconstrainederror': - if (error.constraint.toLowerCase() === 'deviceid') { - errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND; - errorMessage = - "Audio input device with deviceId '" + - ((constraints.audio).deviceId!!).exact + - "' not found"; - } else { - errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR; - errorMessage = "Audio input device doesn't support the value passed for constraint '" + error.constraint + "'"; - } - return new OpenViduError(errorName, errorMessage); - case 'notreadableerror': - errorName = OpenViduErrorName.DEVICE_ALREADY_IN_USE; - errorMessage = error.toString(); - return new OpenViduError(errorName, errorMessage); - default: - return new OpenViduError(OpenViduErrorName.INPUT_AUDIO_DEVICE_GENERIC_ERROR, error.toString()); - } - } - - /** - * @hidden - */ - addAlreadyProvidedTracks(myConstraints: CustomMediaStreamConstraints, mediaStream: MediaStream, stream?: Stream): MediaStream { - if (!!myConstraints.videoTrack) { - mediaStream.addTrack(myConstraints.videoTrack); - if (!!stream) { - if (!!myConstraints.constraints.video) { - stream.lastVideoTrackConstraints = myConstraints.constraints.video; - } else { - stream.lastVideoTrackConstraints = myConstraints.videoTrack.getConstraints(); - } - } - } - if (!!myConstraints.audioTrack) { - mediaStream.addTrack(myConstraints.audioTrack); - } - return mediaStream; - } - - /** - * @hidden - */ - protected configureDeviceIdOrScreensharing( - myConstraints: CustomMediaStreamConstraints, - publisherProperties: PublisherProperties, - resolve, - reject - ) { - const audioSource = publisherProperties.audioSource; - const videoSource = publisherProperties.videoSource; - if (typeof audioSource === 'string' && audioSource !== 'screen') { - myConstraints.constraints!.audio = { deviceId: { exact: audioSource } }; - } - - if (typeof videoSource === 'string') { - if (!this.isScreenShare(videoSource)) { - this.setVideoSource(myConstraints, videoSource); - if (audioSource === 'screen') { - logger.warn('Parameter "audioSource" is set to "screen", which means rquesting audio from screen sharing source. But "videoSource" is not set to "screen". No audio source will be requested'); - myConstraints.constraints!.audio = false; - } - } else { - // Screen sharing - - if (!this.checkScreenSharingCapabilities()) { - const error = new OpenViduError( - OpenViduErrorName.SCREEN_SHARING_NOT_SUPPORTED, - 'You can only screen share in desktop Chrome, Firefox, Opera, Safari (>=13.0), Edge (>= 80) or Electron. Detected client: ' + - platform.getName() + - ' ' + - platform.getVersion() - ); - logger.error(error); - return reject(error); - } else { - if (platform.isElectron()) { - const prefix = 'screen:'; - const videoSourceString: string = videoSource; - const electronScreenId = videoSourceString.substr(videoSourceString.indexOf(prefix) + prefix.length); - (myConstraints.constraints!.video) = { - mandatory: { - chromeMediaSource: 'desktop', - chromeMediaSourceId: electronScreenId - } - }; - return resolve(myConstraints); - } else { - if ( - !!this.advancedConfiguration.screenShareChromeExtension && - !(platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) && - !navigator.mediaDevices['getDisplayMedia'] - ) { - // Custom screen sharing extension for Chrome (and Opera) and no support for MediaDevices.getDisplayMedia() - - screenSharing.getScreenConstraints((error, screenConstraints) => { - if ( - !!error || - (!!screenConstraints.mandatory && screenConstraints.mandatory.chromeMediaSource === 'screen') - ) { - if (error === 'permission-denied' || error === 'PermissionDeniedError') { - const error = new OpenViduError( - OpenViduErrorName.SCREEN_CAPTURE_DENIED, - 'You must allow access to one window of your desktop' - ); - logger.error(error); - return reject(error); - } else { - const extensionId = this.advancedConfiguration - .screenShareChromeExtension!.split('/') - .pop()!! - .trim(); - screenSharing.getChromeExtensionStatus(extensionId, (status) => { - if (status === 'installed-disabled') { - const error = new OpenViduError( - OpenViduErrorName.SCREEN_EXTENSION_DISABLED, - 'You must enable the screen extension' - ); - logger.error(error); - return reject(error); - } - if (status === 'not-installed') { - const error = new OpenViduError( - OpenViduErrorName.SCREEN_EXTENSION_NOT_INSTALLED, - this.advancedConfiguration.screenShareChromeExtension - ); - logger.error(error); - return reject(error); - } - }); - return; - } - } else { - myConstraints.constraints!.video = screenConstraints; - return resolve(myConstraints); - } - }); - return; - } else { - if (navigator.mediaDevices['getDisplayMedia']) { - // getDisplayMedia support (Chrome >= 72, Firefox >= 66, Safari >= 13) - return resolve(myConstraints); - } else { - // Default screen sharing extension for Chrome/Opera, or is Firefox < 66 - const firefoxString = - platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser() - ? publisherProperties.videoSource - : undefined; - - screenSharingAuto.getScreenId(firefoxString, (error, sourceId, screenConstraints) => { - if (!!error) { - if (error === 'not-installed') { - const extensionUrl = !!this.advancedConfiguration.screenShareChromeExtension - ? this.advancedConfiguration.screenShareChromeExtension - : 'https://chrome.google.com/webstore/detail/openvidu-screensharing/lfcgfepafnobdloecchnfaclibenjold'; - const err = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_NOT_INSTALLED, extensionUrl); - logger.error(err); - return reject(err); - } else if (error === 'installed-disabled') { - const err = new OpenViduError( - OpenViduErrorName.SCREEN_EXTENSION_DISABLED, - 'You must enable the screen extension' - ); - logger.error(err); - return reject(err); - } else if (error === 'permission-denied') { - const err = new OpenViduError( - OpenViduErrorName.SCREEN_CAPTURE_DENIED, - 'You must allow access to one window of your desktop' - ); - logger.error(err); - return reject(err); - } else { - const err = new OpenViduError( - OpenViduErrorName.GENERIC_ERROR, - 'Unknown error when accessing screen share' - ); - logger.error(err); - logger.error(error); - return reject(err); - } - } else { - myConstraints.constraints!.video = screenConstraints.video; - return resolve(myConstraints); - } - }); - return; - } - } - } - } - } - } - } - - /** - * @hidden - */ - protected setVideoSource(myConstraints: CustomMediaStreamConstraints, videoSource: string) { - if (!myConstraints.constraints!.video) { - myConstraints.constraints!.video = {}; - } - (myConstraints.constraints!.video)['deviceId'] = { exact: videoSource }; - } - - /* Private methods */ - - private disconnectCallback(): void { - logger.warn('Websocket connection lost'); - if (this.isRoomAvailable()) { - this.session.onLostConnection('networkDisconnect'); - } else { - alert('Connection error. Please reload page.'); - } - } - - private reconnectingCallback(): void { - logger.warn('Websocket connection lost (reconnecting)'); - if (!this.isRoomAvailable()) { - alert('Connection error. Please reload page.'); - } else { - this.session.emitEvent('reconnecting', []); - } - } - - private reconnectWebsocketThroughRpcConnectMethod(rpcSessionId) { - // This RPC method allows checking: - // Single Master: if success, connection recovered - // if error, no Master Node crashed and life will be -1. onLostConnection with reason networkDisconnect will be triggered - // Multi Master: if success, connection recovered - // if error and Master Node crashed notification was already received, nothing must be done - // if error and Master Node NOT crashed, sessionStatus method must be sent: - // if life is equal, networkDisconnect - // if life is greater, nodeCrashed - this.sendRequest('connect', { sessionId: rpcSessionId, reconnect: true }, (error, response) => { - if (!!error) { - if (this.isMasterNodeCrashed()) { - logger.warn('Master Node has crashed!'); - } else { - logger.error(error); - - const notifyLostConnection = (reason, errorMsg) => { - logger.warn(errorMsg); - this.session.onLostConnection(reason); - this.jsonRpcClient.close(4101, 'Reconnection fault: ' + errorMsg); - }; - - const rpcSessionStatus = () => { - if (this.life === -1) { - // Single Master - notifyLostConnection( - 'networkDisconnect', - 'WS successfully reconnected but the user was already evicted due to timeout' - ); - } else { - // Multi Master - // This RPC method is only required to find out the reason of the disconnection: - // whether the client lost its network connection or a Master Node crashed - this.sendRequest('sessionStatus', { sessionId: this.session.sessionId }, (error, response) => { - if (error != null) { - console.error('Error checking session status', error); - } else { - if (this.life === response.life) { - // If the life stored in the client matches the life stored in the server, it means that the client lost its network connection - notifyLostConnection( - 'networkDisconnect', - 'WS successfully reconnected but the user was already evicted due to timeout' - ); - } else { - // If the life stored in the client is below the life stored in the server, it means that the Master Node has crashed - notifyLostConnection( - 'nodeCrashed', - 'WS successfully reconnected to OpenVidu Server but your Master Node crashed' - ); - } - } - }); - } - }; - - if (error.code === 40007 && error.message === 'reconnection error') { - // Kurento error: invalid RPC sessionId. This means that the kurento-jsonrpc-server of openvidu-server where kurento-jsonrpc-client - // is trying to reconnect does not know about this sessionId. This can mean two things: - // 1) openvidu-browser managed to reconnect after a while, but openvidu-server already evicted the user for not receiving ping. - // 2) openvidu-server process is a different one because of a node crash. - // Send a "sessionStatus" method to check the reason - console.error('Invalid RPC sessionId. Client network disconnection or Master Node crash'); - rpcSessionStatus(); - } else { - rpcSessionStatus(); - } - } - } else { - this.jsonRpcClient.resetPing(); - this.session.onRecoveredConnection(); - } - }); - } - - private reconnectedCallback(): void { - logger.warn('Websocket reconnected'); - if (this.isRoomAvailable()) { - if (!!this.session.connection) { - this.reconnectWebsocketThroughRpcConnectMethod(this.session.connection.rpcSessionId); - } else { - logger.warn('There was no previous connection when running reconnection callback'); - // Make Session object dispatch 'sessionDisconnected' event - const sessionDisconnectEvent = new SessionDisconnectedEvent(this.session, 'networkDisconnect'); - this.session.ee.emitEvent('sessionDisconnected', [sessionDisconnectEvent]); - sessionDisconnectEvent.callDefaultBehavior(); - } - } else { - alert('Connection error. Please reload page.'); - } - } - - private isMasterNodeCrashed() { - return this.masterNodeHasCrashed; - } - - private isRoomAvailable(): boolean { - if (this.session !== undefined && this.session instanceof Session) { - return true; - } else { - logger.warn('Session instance not found'); - return false; - } - } - - private isScreenShare(videoSource: string) { - return videoSource === 'screen' || videoSource === 'window' || (platform.isElectron() && videoSource.startsWith('screen:')); - } -} diff --git a/openvidu-browser/src/OpenVidu/Publisher.ts b/openvidu-browser/src/OpenVidu/Publisher.ts deleted file mode 100644 index 4b674904..00000000 --- a/openvidu-browser/src/OpenVidu/Publisher.ts +++ /dev/null @@ -1,875 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { OpenVidu } from './OpenVidu'; -import { Session } from './Session'; -import { Stream } from './Stream'; -import { StreamManager } from './StreamManager'; -import { PublisherProperties } from '../OpenViduInternal/Interfaces/Public/PublisherProperties'; -import { PublisherEventMap } from '../OpenViduInternal/Events/EventMap/PublisherEventMap'; -import { StreamEvent } from '../OpenViduInternal/Events/StreamEvent'; -import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent'; -import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError'; -import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode'; -import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; -import { PlatformUtils } from '../OpenViduInternal/Utils/Platform'; -import { TypeOfVideo } from '../OpenViduInternal/Enums/TypeOfVideo'; -import { StreamEventReason } from '../OpenViduInternal/Events/Types/Types'; - -/** - * @hidden - */ -const logger: OpenViduLogger = OpenViduLogger.getInstance(); - -/** - * @hidden - */ -let platform: PlatformUtils; - -/** - * Packs local media streams. Participants can publish it to a session. Initialized with {@link OpenVidu.initPublisher} method. - * - * See available event listeners at {@link PublisherEventMap}. - */ -export class Publisher extends StreamManager { - /** - * Whether the Publisher has been granted access to the requested input devices or not - */ - accessAllowed = false; - - /** - * Whether you have called {@link Publisher.subscribeToRemote} with value `true` or `false` (*false* by default) - */ - isSubscribedToRemote = false; - - /** - * The {@link Session} to which the Publisher belongs - */ - session: Session; // Initialized by Session.publish(Publisher) - - private accessDenied = false; - protected properties: PublisherProperties; - private permissionDialogTimeout: NodeJS.Timer; - - /** - * @hidden - */ - openvidu: OpenVidu; - /** - * @hidden - */ - videoReference: HTMLVideoElement; - /** - * @hidden - */ - screenShareResizeInterval: NodeJS.Timer; - - /** - * @hidden - */ - constructor(targEl: string | HTMLElement | undefined, properties: PublisherProperties, openvidu: OpenVidu) { - super( - new Stream(!!openvidu.session ? openvidu.session : new Session(openvidu), { - publisherProperties: properties, - mediaConstraints: {} - }), - targEl - ); - platform = PlatformUtils.getInstance(); - this.properties = properties; - this.openvidu = openvidu; - - this.stream.ee.on('local-stream-destroyed', (reason: StreamEventReason) => { - this.stream.isLocalStreamPublished = false; - const streamEvent = new StreamEvent(true, this, 'streamDestroyed', this.stream, reason); - this.emitEvent('streamDestroyed', [streamEvent]); - streamEvent.callDefaultBehavior(); - }); - } - - /** - * Publish or unpublish the audio stream (if available). Calling this method twice in a row passing same `enabled` value will have no effect - * - * #### Events dispatched - * - * > _Only if `Session.publish(Publisher)` has been called for this Publisher_ - * - * The {@link Session} object of the local participant will dispatch a `streamPropertyChanged` event with `changedProperty` set to `"audioActive"` and `reason` set to `"publishAudio"` - * The {@link Publisher} object of the local participant will also dispatch the exact same event - * - * The {@link Session} object of every other participant connected to the session will dispatch a `streamPropertyChanged` event with `changedProperty` set to `"audioActive"` and `reason` set to `"publishAudio"` - * The respective {@link Subscriber} object of every other participant receiving this Publisher's stream will also dispatch the exact same event - * - * See {@link StreamPropertyChangedEvent} to learn more. - * - * @param enabled `true` to publish the audio stream, `false` to unpublish it - */ - publishAudio(enabled: boolean): void { - if (this.stream.audioActive !== enabled) { - const affectedMediaStream: MediaStream = this.stream.displayMyRemote() - ? this.stream.localMediaStreamWhenSubscribedToRemote! - : this.stream.getMediaStream(); - affectedMediaStream.getAudioTracks().forEach((track) => { - track.enabled = enabled; - }); - if (!!this.session && !!this.stream.streamId) { - this.session.openvidu.sendRequest( - 'streamPropertyChanged', - { - streamId: this.stream.streamId, - property: 'audioActive', - newValue: enabled, - reason: 'publishAudio' - }, - (error, response) => { - if (error) { - logger.error("Error sending 'streamPropertyChanged' event", error); - } else { - this.session.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent(this.session, this.stream, 'audioActive', enabled, !enabled, 'publishAudio') - ]); - this.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent(this, this.stream, 'audioActive', enabled, !enabled, 'publishAudio') - ]); - this.session.sendVideoData(this.stream.streamManager); - } - } - ); - } - this.stream.audioActive = enabled; - logger.info("'Publisher' has " + (enabled ? 'published' : 'unpublished') + ' its audio stream'); - } - } - - /** - * Publish or unpublish the video stream (if available). Calling this method twice in a row passing same `enabled` value will have no effect - * - * #### Events dispatched - * - * > _Only if `Session.publish(Publisher)` has been called for this Publisher_ - * - * The {@link Session} object of the local participant will dispatch a `streamPropertyChanged` event with `changedProperty` set to `"videoActive"` and `reason` set to `"publishVideo"` - * The {@link Publisher} object of the local participant will also dispatch the exact same event - * - * The {@link Session} object of every other participant connected to the session will dispatch a `streamPropertyChanged` event with `changedProperty` set to `"videoActive"` and `reason` set to `"publishVideo"` - * The respective {@link Subscriber} object of every other participant receiving this Publisher's stream will also dispatch the exact same event - * - * See {@link StreamPropertyChangedEvent} to learn more. - * - * @param enabled `true` to publish the video stream, `false` to unpublish it - * @param resource - * - * If parameter **`enabled`** is `false`, this optional parameter is of type boolean. It can be set to `true` to forcibly free the hardware resource associated to the video track, or can be set to `false` to keep the access to the hardware resource. - * Not freeing the resource makes the operation much more efficient, but depending on the platform two side-effects can be introduced: the video device may not be accessible by other applications and the access light of - * webcams may remain on. This is platform-dependent: some browsers will not present the side-effects even when not freeing the resource. - * - * If parameter **`enabled`** is `true`, this optional parameter is of type [MediaStreamTrack](https://developer.mozilla.org/docs/Web/API/MediaStreamTrack). It can be set to force the restoration of the video track with a custom track. This may be - * useful if the Publisher was unpublished freeing the hardware resource, and openvidu-browser is not able to successfully re-create the video track as it was before unpublishing. In this way previous track settings will be ignored and this MediaStreamTrack - * will be used instead. - */ - publishVideo(enabled: T, resource?: T extends false ? boolean : MediaStreamTrack): Promise { - return new Promise(async (resolve, reject) => { - if (this.stream.videoActive !== enabled) { - const affectedMediaStream: MediaStream = this.stream.displayMyRemote() - ? this.stream.localMediaStreamWhenSubscribedToRemote! - : this.stream.getMediaStream(); - let mustRestartMediaStream = false; - affectedMediaStream.getVideoTracks().forEach((track) => { - track.enabled = enabled; - if (!enabled && resource === true) { - track.stop(); - } else if (enabled && track.readyState === 'ended') { - // Resource was freed - mustRestartMediaStream = true; - } - }); - - // There is a Virtual Background filter applied that must be removed in case the hardware must be freed - if (!enabled && resource === true && !!this.stream.filter && this.stream.filter.type.startsWith('VB:')) { - this.stream.lastVBFilter = this.stream.filter; // Save the filter to be re-applied in case of unmute - await this.stream.removeFilterAux(true); - } - - if (mustRestartMediaStream) { - const oldVideoTrack = affectedMediaStream.getVideoTracks()[0]; - affectedMediaStream.removeTrack(oldVideoTrack); - - const replaceVideoTrack = async (tr: MediaStreamTrack) => { - affectedMediaStream.addTrack(tr); - if (this.stream.isLocalStreamPublished) { - await this.replaceTrackInRtcRtpSender(tr); - } - if (!!this.stream.lastVBFilter) { - setTimeout(async () => { - let options = this.stream.lastVBFilter!.options; - const lastExecMethod = this.stream.lastVBFilter!.lastExecMethod; - if (!!lastExecMethod && lastExecMethod.method === 'update') { - options = Object.assign({}, options, lastExecMethod.params); - } - await this.stream.applyFilter(this.stream.lastVBFilter!.type, options); - delete this.stream.lastVBFilter; - }, 1); - } - }; - - if (!!resource && resource instanceof MediaStreamTrack) { - await replaceVideoTrack(resource); - } else { - try { - const mediaStream = await navigator.mediaDevices.getUserMedia({ - audio: false, - video: this.stream.lastVideoTrackConstraints - }); - await replaceVideoTrack(mediaStream.getVideoTracks()[0]); - } catch (error) { - return reject(error); - } - } - } - - if (!!this.session && !!this.stream.streamId) { - this.session.openvidu.sendRequest( - 'streamPropertyChanged', - { - streamId: this.stream.streamId, - property: 'videoActive', - newValue: enabled, - reason: 'publishVideo' - }, - (error, response) => { - if (error) { - logger.error("Error sending 'streamPropertyChanged' event", error); - } else { - this.session.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent( - this.session, - this.stream, - 'videoActive', - enabled, - !enabled, - 'publishVideo' - ) - ]); - this.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent(this, this.stream, 'videoActive', enabled, !enabled, 'publishVideo') - ]); - this.session.sendVideoData(this.stream.streamManager); - } - } - ); - } - this.stream.videoActive = enabled; - logger.info("'Publisher' has " + (enabled ? 'published' : 'unpublished') + ' its video stream'); - return resolve(); - } - }); - } - - /** - * Call this method before {@link Session.publish} if you prefer to subscribe to your Publisher's remote stream instead of using the local stream, as any other user would do. - */ - subscribeToRemote(value?: boolean): void { - value = value !== undefined ? value : true; - this.isSubscribedToRemote = value; - this.stream.subscribeToMyRemote(value); - } - - /** - * See {@link EventDispatcher.on} - */ - on(type: K, handler: (event: PublisherEventMap[K]) => void): this { - super.on(type, handler); - - if (type === 'streamCreated') { - if (!!this.stream && this.stream.isLocalStreamPublished) { - this.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', this.stream, '')]); - } else { - this.stream.ee.on('stream-created-by-publisher', () => { - this.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', this.stream, '')]); - }); - } - } - if (type === 'accessAllowed') { - if (this.accessAllowed) { - this.emitEvent('accessAllowed', []); - } - } - if (type === 'accessDenied') { - if (this.accessDenied) { - this.emitEvent('accessDenied', []); - } - } - return this; - } - - /** - * See {@link EventDispatcher.once} - */ - once(type: K, handler: (event: PublisherEventMap[K]) => void): this { - super.once(type, handler); - - if (type === 'streamCreated') { - if (!!this.stream && this.stream.isLocalStreamPublished) { - this.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', this.stream, '')]); - } else { - this.stream.ee.once('stream-created-by-publisher', () => { - this.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', this.stream, '')]); - }); - } - } - if (type === 'accessAllowed') { - if (this.accessAllowed) { - this.emitEvent('accessAllowed', []); - } - } - if (type === 'accessDenied') { - if (this.accessDenied) { - this.emitEvent('accessDenied', []); - } - } - return this; - } - - /** - * See {@link EventDispatcher.off} - */ - off(type: K, handler?: (event: PublisherEventMap[K]) => void): this { - super.off(type, handler); - return this; - } - - /** - * Replaces the current video or audio track with a different one. This allows you to replace an ongoing track with a different one - * without having to renegotiate the whole WebRTC connection (that is, initializing a new Publisher, unpublishing the previous one - * and publishing the new one). - * - * You can get this new MediaStreamTrack by using the native Web API or simply with {@link OpenVidu.getUserMedia} method. - * - * **WARNING: this method has been proven to work in the majority of cases, but there may be some combinations of published/replaced tracks that may be incompatible - * between them and break the connection in OpenVidu Server. A complete renegotiation may be the only solution in this case. - * Visit [RTCRtpSender.replaceTrack](https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpSender/replaceTrack) documentation for further details.** - * - * @param track The [MediaStreamTrack](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack) object to replace the current one. - * If it is an audio track, the current audio track will be the replaced one. If it is a video track, the current video track will be the replaced one. - * - * @returns A Promise (to which you can optionally subscribe to) that is resolved if the track was successfully replaced and rejected with an Error object in other case - */ - async replaceTrack(track: MediaStreamTrack): Promise { - return this.replaceTrackAux(track, true); - } - - /* Hidden methods */ - - /** - * @hidden - */ - initialize(): Promise { - return new Promise(async (resolve, reject) => { - let constraints: MediaStreamConstraints = {}; - let constraintsAux: MediaStreamConstraints = {}; - const timeForDialogEvent = 2000; - let startTime; - - const errorCallback = (openViduError: OpenViduError) => { - this.accessDenied = true; - this.accessAllowed = false; - logger.error(`Publisher initialization failed. ${openViduError.name}: ${openViduError.message}`); - return reject(openViduError); - }; - - const successCallback = (mediaStream: MediaStream) => { - this.accessAllowed = true; - this.accessDenied = false; - - if (typeof MediaStreamTrack !== 'undefined' && this.properties.audioSource instanceof MediaStreamTrack) { - mediaStream.removeTrack(mediaStream.getAudioTracks()[0]); - mediaStream.addTrack(this.properties.audioSource); - } - - if (typeof MediaStreamTrack !== 'undefined' && this.properties.videoSource instanceof MediaStreamTrack) { - mediaStream.removeTrack(mediaStream.getVideoTracks()[0]); - mediaStream.addTrack(this.properties.videoSource); - } - - // Apply PublisherProperties.publishAudio and PublisherProperties.publishVideo - if (!!mediaStream.getAudioTracks()[0]) { - const enabled = - this.stream.audioActive !== undefined && this.stream.audioActive !== null - ? this.stream.audioActive - : !!this.stream.outboundStreamOpts.publisherProperties.publishAudio; - mediaStream.getAudioTracks()[0].enabled = enabled; - } - if (!!mediaStream.getVideoTracks()[0]) { - const enabled = - this.stream.videoActive !== undefined && this.stream.videoActive !== null - ? this.stream.videoActive - : !!this.stream.outboundStreamOpts.publisherProperties.publishVideo; - mediaStream.getVideoTracks()[0].enabled = enabled; - } - - // Set Content Hint on all MediaStreamTracks - for (const track of mediaStream.getAudioTracks()) { - if (!track.contentHint?.length) { - // contentHint for audio: "", "speech", "speech-recognition", "music". - // https://w3c.github.io/mst-content-hint/#audio-content-hints - track.contentHint = ''; - logger.info(`Audio track Content Hint set: '${track.contentHint}'`); - } - } - for (const track of mediaStream.getVideoTracks()) { - if (!track.contentHint?.length) { - // contentHint for video: "", "motion", "detail", "text". - // https://w3c.github.io/mst-content-hint/#video-content-hints - switch (this.stream.typeOfVideo) { - case TypeOfVideo.SCREEN: - track.contentHint = 'detail'; - break; - case TypeOfVideo.CUSTOM: - logger.warn('CUSTOM type video track was provided without Content Hint!'); - track.contentHint = 'motion'; - break; - case TypeOfVideo.CAMERA: - case TypeOfVideo.IPCAM: - default: - track.contentHint = 'motion'; - break; - } - logger.info(`Video track Content Hint set: '${track.contentHint}'`); - } - } - - this.initializeVideoReference(mediaStream); - - if (!this.stream.displayMyRemote()) { - // When we are subscribed to our remote we don't still set the MediaStream object in the video elements to - // avoid early 'streamPlaying' event - this.stream.updateMediaStreamInVideos(); - } - delete this.firstVideoElement; - - if (this.stream.isSendVideo()) { - // Has video track - this.getVideoDimensions().then((dimensions) => { - this.stream.videoDimensions = { - width: dimensions.width, - height: dimensions.height - }; - - if (this.stream.isSendScreen()) { - - if(this.stream.isSendAudio() && mediaStream.getAudioTracks().length === 0){ - // If sending audio is enabled and there are no audio tracks in the mediaStream, disable audio for screen sharing. - this.stream.audioActive = false; - this.stream.hasAudio = false; - this.stream.outboundStreamOpts.publisherProperties.publishAudio = false; - this.stream.outboundStreamOpts.publisherProperties.audioSource = false; - } - - // Set interval to listen for screen resize events - this.screenShareResizeInterval = setInterval(() => { - const settings: MediaTrackSettings = mediaStream.getVideoTracks()[0].getSettings(); - const newWidth = settings.width; - const newHeight = settings.height; - const widthChanged = newWidth != null && newWidth !== this.stream.videoDimensions.width; - const heightChanged = newHeight != null && newHeight !== this.stream.videoDimensions.height; - if (this.stream.isLocalStreamPublished && (widthChanged || heightChanged)) { - this.openvidu.sendVideoDimensionsChangedEvent( - this, - 'screenResized', - this.stream.videoDimensions.width, - this.stream.videoDimensions.height, - newWidth || 0, - newHeight || 0 - ); - } - }, 650); - } - - this.stream.isLocalStreamReadyToPublish = true; - this.stream.ee.emitEvent('stream-ready-to-publish', []); - }); - } else { - // Only audio track (no videoDimensions) - this.stream.isLocalStreamReadyToPublish = true; - this.stream.ee.emitEvent('stream-ready-to-publish', []); - } - - return resolve(); - }; - - const getMediaSuccess = async (mediaStream: MediaStream, definedAudioConstraint) => { - this.clearPermissionDialogTimer(startTime, timeForDialogEvent); - if (this.stream.isSendScreen() && this.properties.audioSource !== 'screen' && this.stream.isSendAudio()) { - // When getting desktop as user media audio constraint must be false. Now we can ask for it if required - constraintsAux.audio = definedAudioConstraint; - constraintsAux.video = false; - startTime = Date.now(); - this.setPermissionDialogTimer(timeForDialogEvent); - - try { - const audioOnlyStream = await navigator.mediaDevices.getUserMedia(constraintsAux); - this.clearPermissionDialogTimer(startTime, timeForDialogEvent); - mediaStream.addTrack(audioOnlyStream.getAudioTracks()[0]); - successCallback(mediaStream); - } catch (error) { - this.clearPermissionDialogTimer(startTime, timeForDialogEvent); - mediaStream.getAudioTracks().forEach((track) => { - track.stop(); - }); - mediaStream.getVideoTracks().forEach((track) => { - track.stop(); - }); - errorCallback(this.openvidu.generateAudioDeviceError(error, constraints)); - return; - } - } else { - successCallback(mediaStream); - } - }; - - const getMediaError = async (error) => { - logger.error(`getMediaError: ${error.toString()}`); - this.clearPermissionDialogTimer(startTime, timeForDialogEvent); - if (error.name === 'Error') { - // Safari OverConstrainedError has as name property 'Error' instead of 'OverConstrainedError' - error.name = error.constructor.name; - } - let errorName, errorMessage; - switch (error.name.toLowerCase()) { - case 'notfounderror': - try { - const mediaStream = await navigator.mediaDevices.getUserMedia({ - audio: false, - video: constraints.video - }); - mediaStream.getVideoTracks().forEach((track) => { - track.stop(); - }); - errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND; - errorMessage = error.toString(); - errorCallback(new OpenViduError(errorName, errorMessage)); - } catch (error) { - errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND; - errorMessage = error.toString(); - errorCallback(new OpenViduError(errorName, errorMessage)); - } - - break; - case 'notallowederror': - errorName = this.stream.isSendScreen() - ? OpenViduErrorName.SCREEN_CAPTURE_DENIED - : OpenViduErrorName.DEVICE_ACCESS_DENIED; - errorMessage = error.toString(); - errorCallback(new OpenViduError(errorName, errorMessage)); - break; - case 'overconstrainederror': - try { - const mediaStream = await navigator.mediaDevices.getUserMedia({ - audio: false, - video: constraints.video - }); - mediaStream.getVideoTracks().forEach((track) => { - track.stop(); - }); - if (error.constraint.toLowerCase() === 'deviceid') { - errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND; - errorMessage = - "Audio input device with deviceId '" + - ((constraints.audio).deviceId!!).exact + - "' not found"; - } else { - errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR; - errorMessage = - "Audio input device doesn't support the value passed for constraint '" + error.constraint + "'"; - } - errorCallback(new OpenViduError(errorName, errorMessage)); - } catch (error) { - if (error.constraint.toLowerCase() === 'deviceid') { - errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND; - errorMessage = - "Video input device with deviceId '" + - ((constraints.video).deviceId!!).exact + - "' not found"; - } else { - errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR; - errorMessage = - "Video input device doesn't support the value passed for constraint '" + error.constraint + "'"; - } - errorCallback(new OpenViduError(errorName, errorMessage)); - } - - break; - case 'aborterror': - case 'notreadableerror': - errorName = OpenViduErrorName.DEVICE_ALREADY_IN_USE; - errorMessage = error.toString(); - errorCallback(new OpenViduError(errorName, errorMessage)); - break; - default: - errorName = OpenViduErrorName.GENERIC_ERROR; - errorMessage = error.toString(); - errorCallback(new OpenViduError(errorName, errorMessage)); - break; - } - }; - - try { - const myConstraints = await this.openvidu.generateMediaConstraints(this.properties); - if ( - (!!myConstraints.videoTrack && !!myConstraints.audioTrack) || - (!!myConstraints.audioTrack && myConstraints.constraints?.video === false) || - (!!myConstraints.videoTrack && myConstraints.constraints?.audio === false) - ) { - // No need to call getUserMedia at all. MediaStreamTracks already provided - successCallback(this.openvidu.addAlreadyProvidedTracks(myConstraints, new MediaStream(), this.stream)); - } else { - constraints = myConstraints.constraints; - - const outboundStreamOptions = { - mediaConstraints: constraints, - publisherProperties: this.properties - }; - this.stream.setOutboundStreamOptions(outboundStreamOptions); - - const definedAudioConstraint = constraints.audio === undefined ? true : constraints.audio; - constraintsAux.audio = this.stream.isSendScreen() ? false : definedAudioConstraint; - constraintsAux.video = constraints.video; - startTime = Date.now(); - this.setPermissionDialogTimer(timeForDialogEvent); - - try { - if (this.stream.isSendScreen() && navigator.mediaDevices['getDisplayMedia'] && !platform.isElectron()) { - const mediaStream = await navigator.mediaDevices['getDisplayMedia']({ video: true, audio: this.properties.audioSource === 'screen' }); - this.openvidu.addAlreadyProvidedTracks(myConstraints, mediaStream); - await getMediaSuccess(mediaStream, definedAudioConstraint); - } else { - this.stream.lastVideoTrackConstraints = constraintsAux.video; - const mediaStream = await navigator.mediaDevices.getUserMedia(constraintsAux); - this.openvidu.addAlreadyProvidedTracks(myConstraints, mediaStream, this.stream); - await getMediaSuccess(mediaStream, definedAudioConstraint); - } - } catch (error) { - await getMediaError(error); - } - } - } catch (error) { - errorCallback(error); - } - }); - } - - /** - * @hidden - */ - async replaceTrackAux(track: MediaStreamTrack, updateLastConstraints: boolean): Promise { - // Set field "enabled" of the new track to the previous value - const trackOriginalEnabledValue: boolean = track.enabled; - if (track.kind === 'video') { - track.enabled = this.stream.videoActive; - } else if (track.kind === 'audio') { - track.enabled = this.stream.audioActive; - } - try { - if (this.stream.isLocalStreamPublished) { - // Only if the Publisher has been published is necessary to call native Web API RTCRtpSender.replaceTrack - // If it has not been published yet, replacing it on the MediaStream object is enough - this.replaceTrackInMediaStream(track, updateLastConstraints); - return await this.replaceTrackInRtcRtpSender(track); - } else { - // Publisher not published. Simply replace the track on the local MediaStream - return this.replaceTrackInMediaStream(track, updateLastConstraints); - } - } catch (error) { - track.enabled = trackOriginalEnabledValue; - throw error; - } - } - - /** - * @hidden - * - * To obtain the videoDimensions we wait for the video reference to have enough metadata - * and then try to use MediaStreamTrack.getSettingsMethod(). If not available, then we - * use the HTMLVideoElement properties videoWidth and videoHeight - */ - getVideoDimensions(): Promise<{ width: number; height: number }> { - return new Promise((resolve, reject) => { - // Ionic iOS and Safari iOS supposedly require the video element to actually exist inside the DOM - const requiresDomInsertion: boolean = (platform.isIonicIos() || platform.isIOSWithSafari()) && (this.videoReference.readyState < 1); - - let loadedmetadataListener; - const resolveDimensions = () => { - let width: number; - let height: number; - if (typeof this.stream.getMediaStream().getVideoTracks()[0].getSettings === 'function') { - const settings = this.stream.getMediaStream().getVideoTracks()[0].getSettings(); - width = settings.width || this.videoReference.videoWidth; - height = settings.height || this.videoReference.videoHeight; - } else { - logger.warn('MediaStreamTrack does not have getSettings method on ' + platform.getDescription()); - width = this.videoReference.videoWidth; - height = this.videoReference.videoHeight; - } - - if (loadedmetadataListener != null) { - this.videoReference.removeEventListener('loadedmetadata', loadedmetadataListener); - } - if (requiresDomInsertion) { - document.body.removeChild(this.videoReference); - } - - return resolve({ width, height }); - }; - - if (this.videoReference.readyState >= 1) { - // The video already has metadata available - // No need of loadedmetadata event - resolveDimensions(); - } else { - // The video does not have metadata available yet - // Must listen to loadedmetadata event - loadedmetadataListener = () => { - if (!this.videoReference.videoWidth) { - let interval = setInterval(() => { - if (!!this.videoReference.videoWidth) { - clearInterval(interval); - resolveDimensions(); - } - }, 40); - } else { - resolveDimensions(); - } - }; - this.videoReference.addEventListener('loadedmetadata', loadedmetadataListener); - if (requiresDomInsertion) { - document.body.appendChild(this.videoReference); - } - } - }); - } - - /** - * @hidden - */ - reestablishStreamPlayingEvent() { - if (this.ee.getListeners('streamPlaying').length > 0) { - this.addPlayEventToFirstVideo(); - } - } - - /** - * @hidden - */ - initializeVideoReference(mediaStream: MediaStream) { - this.videoReference = document.createElement('video'); - this.videoReference.style.display = 'none'; - this.videoReference.muted = true; - this.videoReference.autoplay = true; - this.videoReference.controls = false; - if ( - platform.isSafariBrowser() || - (platform.isIPhoneOrIPad() && - (platform.isChromeMobileBrowser() || - platform.isEdgeMobileBrowser() || - platform.isOperaMobileBrowser() || - platform.isFirefoxMobileBrowser())) - ) { - this.videoReference.playsInline = true; - } - this.stream.setMediaStream(mediaStream); - if (!!this.firstVideoElement) { - this.createVideoElement(this.firstVideoElement.targetElement, this.properties.insertMode); - } - this.videoReference.srcObject = this.stream.getMediaStream(); - } - - /** - * @hidden - */ - replaceTrackInMediaStream(track: MediaStreamTrack, updateLastConstraints: boolean): void { - const mediaStream: MediaStream = this.stream.displayMyRemote() - ? this.stream.localMediaStreamWhenSubscribedToRemote! - : this.stream.getMediaStream(); - let removedTrack: MediaStreamTrack; - if (track.kind === 'video') { - removedTrack = mediaStream.getVideoTracks()[0]; - if (updateLastConstraints) { - this.stream.lastVideoTrackConstraints = track.getConstraints(); - } - } else { - removedTrack = mediaStream.getAudioTracks()[0]; - } - removedTrack.enabled = false; - removedTrack.stop(); - mediaStream.removeTrack(removedTrack); - mediaStream.addTrack(track); - const trackInfo = { - oldLabel: removedTrack?.label || '', - newLabel: track?.label || '' - }; - if (track.kind === 'video' && updateLastConstraints) { - this.openvidu.sendNewVideoDimensionsIfRequired(this, 'trackReplaced', 50, 30); - this.openvidu.sendTrackChangedEvent(this, trackInfo.oldLabel, trackInfo.newLabel, 'videoTrack'); - if (this.stream.isLocalStreamPublished) { - this.session.sendVideoData(this.stream.streamManager, 5, true, 5); - } - } else if (track.kind === 'audio' && updateLastConstraints) { - this.openvidu.sendTrackChangedEvent(this, trackInfo.oldLabel, trackInfo.newLabel, 'audioTrack'); - } - if (track.kind === 'audio') { - this.stream.disableHarkSpeakingEvent(false); - this.stream.disableHarkStoppedSpeakingEvent(false); - this.stream.disableHarkVolumeChangeEvent(false); - this.stream.initHarkEvents(); - } - } - - /* Private methods */ - - private setPermissionDialogTimer(waitTime: number): void { - this.permissionDialogTimeout = setTimeout(() => { - this.emitEvent('accessDialogOpened', []); - }, waitTime); - } - - private clearPermissionDialogTimer(startTime: number, waitTime: number): void { - clearTimeout(this.permissionDialogTimeout); - if (Date.now() - startTime > waitTime) { - // Permission dialog was shown and now is closed - this.emitEvent('accessDialogClosed', []); - } - } - - private async replaceTrackInRtcRtpSender(track: MediaStreamTrack): Promise { - const senders: RTCRtpSender[] = this.stream.getRTCPeerConnection().getSenders(); - let sender: RTCRtpSender | undefined; - if (track.kind === 'video') { - sender = senders.find((s) => !!s.track && s.track.kind === 'video'); - if (!sender) { - throw new Error("There's no replaceable track for that kind of MediaStreamTrack in this Publisher object"); - } - } else if (track.kind === 'audio') { - sender = senders.find((s) => !!s.track && s.track.kind === 'audio'); - if (!sender) { - throw new Error("There's no replaceable track for that kind of MediaStreamTrack in this Publisher object"); - } - } else { - throw new Error('Unknown track kind ' + track.kind); - } - await (sender as RTCRtpSender).replaceTrack(track); - } -} diff --git a/openvidu-browser/src/OpenVidu/Session.ts b/openvidu-browser/src/OpenVidu/Session.ts deleted file mode 100644 index db40b986..00000000 --- a/openvidu-browser/src/OpenVidu/Session.ts +++ /dev/null @@ -1,1804 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Connection } from './Connection'; -import { Filter } from './Filter'; -import { OpenVidu } from './OpenVidu'; -import { Publisher } from './Publisher'; -import { Stream } from './Stream'; -import { StreamManager } from './StreamManager'; -import { Subscriber } from './Subscriber'; -import { Capabilities } from '../OpenViduInternal/Interfaces/Public/Capabilities'; -import { EventDispatcher } from './EventDispatcher'; -import { SignalOptions } from '../OpenViduInternal/Interfaces/Public/SignalOptions'; -import { SubscriberProperties } from '../OpenViduInternal/Interfaces/Public/SubscriberProperties'; -import { RemoteConnectionOptions } from '../OpenViduInternal/Interfaces/Private/RemoteConnectionOptions'; -import { LocalConnectionOptions } from '../OpenViduInternal/Interfaces/Private/LocalConnectionOptions'; -import { SessionOptions } from '../OpenViduInternal/Interfaces/Private/SessionOptions'; -import { SessionEventMap } from '../OpenViduInternal/Events/EventMap/SessionEventMap'; -import { ConnectionEvent } from '../OpenViduInternal/Events/ConnectionEvent'; -import { FilterEvent } from '../OpenViduInternal/Events/FilterEvent'; -import { RecordingEvent } from '../OpenViduInternal/Events/RecordingEvent'; -import { SessionDisconnectedEvent } from '../OpenViduInternal/Events/SessionDisconnectedEvent'; -import { SignalEvent } from '../OpenViduInternal/Events/SignalEvent'; -import { SpeechToTextEvent } from '../OpenViduInternal/Events/SpeechToTextEvent'; -import { StreamEvent } from '../OpenViduInternal/Events/StreamEvent'; -import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent'; -import { ConnectionPropertyChangedEvent } from '../OpenViduInternal/Events/ConnectionPropertyChangedEvent'; -import { NetworkQualityLevelChangedEvent } from '../OpenViduInternal/Events/NetworkQualityLevelChangedEvent'; -import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError'; -import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode'; -import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; -import { PlatformUtils } from '../OpenViduInternal/Utils/Platform'; -import { StreamPropertyChangedEventReason, ChangedPropertyType, RecordingEventReason, ConnectionEventReason, StreamEventReason } from '../OpenViduInternal/Events/Types/Types'; -/** - * @hidden - */ -import semverMajor = require('semver/functions/major'); -/** - * @hidden - */ -import semverMinor = require('semver/functions/minor'); -import { ExceptionEvent, ExceptionEventName } from '../OpenViduInternal/Events/ExceptionEvent'; - -/** - * @hidden - */ -const logger: OpenViduLogger = OpenViduLogger.getInstance(); - -/** - * @hidden - */ -let platform: PlatformUtils; - -/** - * Represents a video call. It can also be seen as a videoconference room where multiple users can connect. - * Participants who publish their videos to a session can be seen by the rest of users connected to that specific session. - * Initialized with {@link OpenVidu.initSession} method. - * - * See available event listeners at {@link SessionEventMap}. - */ -export class Session extends EventDispatcher { - /** - * Local connection to the Session. This object is defined only after {@link Session.connect} has been successfully executed, and can be retrieved subscribing to `connectionCreated` event - */ - connection: Connection; - - /** - * Unique identifier of the Session - */ - sessionId: string; - - /** - * Collection of all StreamManagers of this Session ({@link Publisher} and {@link Subscriber}) - */ - streamManagers: StreamManager[] = []; - - /** - * Object defining the methods that the client is able to call. These are defined by the {@link Connection.role}. - * This object is only defined after {@link Session.connect} has been successfully resolved - */ - capabilities: Capabilities; - - // This map is only used to avoid race condition between 'joinRoom' response and 'onParticipantPublished' notification - /** - * @hidden - */ - remoteStreamsCreated: Map = new Map(); - - /** - * @hidden - */ - remoteConnections: Map = new Map(); - /** - * @hidden - */ - openvidu: OpenVidu; - /** - * @hidden - */ - options: SessionOptions; - /** - * @hidden - */ - token: string; - /** - * @hidden - */ - private videoDataInterval: NodeJS.Timeout; - /** - * @hidden - */ - private videoDataTimeout: NodeJS.Timeout; - - /** - * @hidden - */ - constructor(openvidu: OpenVidu) { - super(); - platform = PlatformUtils.getInstance(); - this.openvidu = openvidu; - } - - connect(token: string): Promise; - connect(token: string, metadata: any): Promise; - - /** - * Connects to the session using `token`. Parameter `metadata` allows you to pass extra data to share with other users when - * they receive `streamCreated` event. The structure of `metadata` string is up to you (maybe some standardized format - * as JSON or XML is a good idea). - * - * This metadata is not considered secure, as it is generated in the client side. To pass secure data, add it as a parameter in the - * token generation operation (through the API REST, openvidu-java-client or openvidu-node-client). - * - * Only after the returned Promise is successfully resolved {@link Session.connection} object will be available and properly defined. - * - * #### Events dispatched - * - * The {@link Session} object of the local participant will first dispatch one or more `connectionCreated` events upon successful termination of this method: - * - First one for your own local Connection object, so you can retrieve {@link Session.connection} property. - * - Then one for each remote Connection previously connected to the Session, if any. Any other remote user connecting to the Session after you have - * successfully connected will also dispatch a `connectionCreated` event when they do so. - * - * The {@link Session} object of the local participant will also dispatch a `streamCreated` event for each remote active {@link Publisher} that was already streaming - * when connecting, just after dispatching all remote `connectionCreated` events. - * - * The {@link Session} object of every other participant connected to the session will dispatch a `connectionCreated` event. - * - * See {@link ConnectionEvent} and {@link StreamEvent} to learn more. - * - * @returns A Promise to which you must subscribe that is resolved if the the connection to the Session was successful and rejected with an Error object if not - * - */ - connect(token: string, metadata?: any): Promise { - return new Promise((resolve, reject) => { - this.processToken(token); - - if (this.openvidu.checkSystemRequirements()) { - // Early configuration to deactivate automatic subscription to streams - this.options = { - sessionId: this.sessionId, - participantId: token, - metadata: !!metadata ? this.stringClientMetadata(metadata) : '' - }; - this.connectAux(token) - .then(() => resolve()) - .catch((error) => reject(error)); - } else { - return reject( - new OpenViduError( - OpenViduErrorName.BROWSER_NOT_SUPPORTED, - 'Browser ' + - platform.getName() + - ' (version ' + - platform.getVersion() + - ') for ' + - platform.getFamily() + - ' is not supported in OpenVidu' - ) - ); - } - }); - } - - /** - * Leaves the session, destroying all streams and deleting the user as a participant. - * - * #### Events dispatched - * - * The {@link Session} object of the local participant will dispatch a `sessionDisconnected` event. - * This event will automatically unsubscribe the leaving participant from every Subscriber object of the session (this includes closing the RTCPeerConnection and disposing all MediaStreamTracks) - * and also deletes any HTML video element associated to each Subscriber (only those [created by OpenVidu Browser](/en/stable/cheatsheet/manage-videos/#let-openvidu-take-care-of-the-video-players)). - * For every video removed, each Subscriber object will dispatch a `videoElementDestroyed` event. - * Call `event.preventDefault()` upon event `sessionDisconnected` to avoid this behavior and take care of disposing and cleaning all the Subscriber objects yourself. - * See {@link SessionDisconnectedEvent} and {@link VideoElementEvent} to learn more. - * - * The {@link Publisher} object of the local participant will dispatch a `streamDestroyed` event if there is a {@link Publisher} object publishing to the session. - * This event will automatically stop all media tracks and delete any HTML video element associated to it (only those [created by OpenVidu Browser](/en/stable/cheatsheet/manage-videos/#let-openvidu-take-care-of-the-video-players)). - * For every video removed, the Publisher object will dispatch a `videoElementDestroyed` event. - * Call `event.preventDefault()` upon event `streamDestroyed` if you want to clean the Publisher object on your own or re-publish it in a different Session (to do so it is a mandatory requirement to call `Session.unpublish()` - * or/and `Session.disconnect()` in the previous session). See {@link StreamEvent} and {@link VideoElementEvent} to learn more. - * - * The {@link Session} object of every other participant connected to the session will dispatch a `streamDestroyed` event if the disconnected participant was publishing. - * This event will automatically unsubscribe the Subscriber object from the session (this includes closing the RTCPeerConnection and disposing all MediaStreamTracks) - * and also deletes any HTML video element associated to that Subscriber (only those [created by OpenVidu Browser](/en/stable/cheatsheet/manage-videos/#let-openvidu-take-care-of-the-video-players)). - * For every video removed, the Subscriber object will dispatch a `videoElementDestroyed` event. - * Call `event.preventDefault()` upon event `streamDestroyed` to avoid this default behavior and take care of disposing and cleaning the Subscriber object yourself. - * See {@link StreamEvent} and {@link VideoElementEvent} to learn more. - * - * The {@link Session} object of every other participant connected to the session will dispatch a `connectionDestroyed` event in any case. See {@link ConnectionEvent} to learn more. - */ - disconnect(): void { - this.leave(false, 'disconnect'); - } - - subscribe(stream: Stream, targetElement: string | HTMLElement | undefined): Subscriber; - subscribe(stream: Stream, targetElement: string | HTMLElement | undefined, properties: SubscriberProperties): Subscriber; - subscribe( - stream: Stream, - targetElement: string | HTMLElement | undefined, - completionHandler: (error: Error | undefined) => void - ): Subscriber; - subscribe( - stream: Stream, - targetElement: string | HTMLElement | undefined, - properties: SubscriberProperties, - completionHandler: (error: Error | undefined) => void - ): Subscriber; - - /** - * Subscribes to a `stream`, adding a new HTML video element to DOM with `subscriberProperties` settings. This method is usually called in the callback of `streamCreated` event. - * - * #### Events dispatched - * - * The {@link Subscriber} object will dispatch a `videoElementCreated` event once the HTML video element has been added to DOM (only if you - * [let OpenVidu take care of the video players](/en/stable/cheatsheet/manage-videos/#let-openvidu-take-care-of-the-video-players)). See {@link VideoElementEvent} to learn more. - * - * The {@link Subscriber} object will dispatch a `streamPlaying` event once the remote stream starts playing. See {@link StreamManagerEvent} to learn more. - * - * @param stream Stream object to subscribe to - * @param targetElement HTML DOM element (or its `id` attribute) in which the video element of the Subscriber will be inserted (see {@link SubscriberProperties.insertMode}). If *null* or *undefined* no default video will be created for this Subscriber. - * You can always call method {@link Subscriber.addVideoElement} or {@link Subscriber.createVideoElement} to manage the video elements on your own (see [Manage video players](/en/stable/cheatsheet/manage-videos) section) - * @param completionHandler `error` parameter is null if `subscribe` succeeds, and is defined if it fails. - */ - subscribe( - stream: Stream, - targetElement: string | HTMLElement | undefined, - param3?: ((error: Error | undefined) => void) | SubscriberProperties, - param4?: (error: Error | undefined) => void - ): Subscriber { - let properties: SubscriberProperties = {}; - if (!!param3 && typeof param3 !== 'function') { - properties = { - insertMode: - typeof param3.insertMode !== 'undefined' - ? typeof param3.insertMode === 'string' - ? VideoInsertMode[param3.insertMode] - : properties.insertMode - : VideoInsertMode.APPEND, - subscribeToAudio: typeof param3.subscribeToAudio !== 'undefined' ? param3.subscribeToAudio : true, - subscribeToVideo: typeof param3.subscribeToVideo !== 'undefined' ? param3.subscribeToVideo : true - }; - } else { - properties = { - insertMode: VideoInsertMode.APPEND, - subscribeToAudio: true, - subscribeToVideo: true - }; - } - - let completionHandler: ((error: Error | undefined) => void) | undefined = undefined; - if (!!param3 && typeof param3 === 'function') { - completionHandler = param3; - } else if (!!param4) { - completionHandler = param4; - } - - if (!this.sessionConnected()) { - if (completionHandler !== undefined) { - completionHandler(this.notConnectedError()); - } - throw this.notConnectedError(); - } - - logger.info('Subscribing to ' + stream.connection.connectionId); - - stream - .subscribe() - .then(() => { - logger.info('Subscribed correctly to ' + stream.connection.connectionId); - if (completionHandler !== undefined) { - completionHandler(undefined); - } - }) - .catch((error) => { - if (completionHandler !== undefined) { - completionHandler(error); - } - }); - const subscriber = new Subscriber(stream, targetElement, properties); - if (!!subscriber.targetElement) { - stream.streamManager.createVideoElement(subscriber.targetElement, properties.insertMode); - } - return subscriber; - } - - /** - * Promisified version of {@link Session.subscribe} - */ - subscribeAsync(stream: Stream, targetElement: string | HTMLElement): Promise; - subscribeAsync(stream: Stream, targetElement: string | HTMLElement, properties: SubscriberProperties): Promise; - - subscribeAsync(stream: Stream, targetElement: string | HTMLElement, properties?: SubscriberProperties): Promise { - return new Promise((resolve, reject) => { - if (!this.sessionConnected()) { - return reject(this.notConnectedError()); - } - - let subscriber: Subscriber; - - const callback = (error: Error) => { - if (!!error) { - return reject(error); - } else { - return resolve(subscriber); - } - }; - - if (!!properties) { - subscriber = this.subscribe(stream, targetElement, properties, callback); - } else { - subscriber = this.subscribe(stream, targetElement, callback); - } - }); - } - - /** - * Unsubscribes from `subscriber`, automatically removing its associated HTML video elements. - * - * #### Events dispatched - * - * The {@link Subscriber} object will dispatch a `videoElementDestroyed` event for each video associated to it that was removed from DOM. - * Only videos [created by OpenVidu Browser](/en/stable/cheatsheet/manage-videos/#let-openvidu-take-care-of-the-video-players)) will be automatically removed - * - * See {@link VideoElementEvent} to learn more - */ - unsubscribe(subscriber: Subscriber): Promise { - return new Promise((resolve, reject) => { - if (!this.sessionConnected()) { - return reject(this.notConnectedError()); - } else { - const connectionId = subscriber.stream.connection.connectionId; - - logger.info('Unsubscribing from ' + connectionId); - - this.openvidu.sendRequest( - 'unsubscribeFromVideo', - { sender: subscriber.stream.connection.connectionId }, - (error, response) => { - if (error) { - logger.error('Error unsubscribing from ' + connectionId); - return reject(error); - } else { - logger.info('Unsubscribed correctly from ' + connectionId); - subscriber.stream.streamManager.removeAllVideos(); - subscriber.stream.disposeWebRtcPeer(); - subscriber.stream.disposeMediaStream(); - return resolve(); - } - } - ); - } - }); - } - - /** - * Publishes to the Session the Publisher object - * - * #### Events dispatched - * - * The local {@link Publisher} object will dispatch a `streamCreated` event upon successful termination of this method. See {@link StreamEvent} to learn more. - * - * The local {@link Publisher} object will dispatch a `streamPlaying` once the media stream starts playing. See {@link StreamManagerEvent} to learn more. - * - * The {@link Session} object of every other participant connected to the session will dispatch a `streamCreated` event so they can subscribe to it. See {@link StreamEvent} to learn more. - * - * @returns A Promise (to which you can optionally subscribe to) that is resolved only after the publisher was successfully published and rejected with an Error object if not - */ - publish(publisher: Publisher): Promise { - return new Promise((resolve, reject) => { - if (!this.sessionConnected()) { - return reject(this.notConnectedError()); - } - - publisher.session = this; - publisher.stream.session = this; - - if (!publisher.stream.publishedOnce) { - // 'Session.unpublish(Publisher)' has NOT been called - this.connection.addStream(publisher.stream); - publisher.stream - .publish() - .then(() => { - this.sendVideoData(publisher, 8, true, 5); - return resolve(); - }) - .catch((error) => reject(error)); - } else { - // 'Session.unpublish(Publisher)' has been called. Must initialize again Publisher - publisher - .initialize() - .then(() => { - this.connection.addStream(publisher.stream); - publisher.reestablishStreamPlayingEvent(); - publisher.stream - .publish() - .then(() => { - this.sendVideoData(publisher, 8, true, 5); - return resolve(); - }) - .catch((error) => reject(error)); - }) - .catch((error) => reject(error)); - } - }); - } - - /** - * Unpublishes from the Session the Publisher object. - * - * #### Events dispatched - * - * The {@link Publisher} object of the local participant will dispatch a `streamDestroyed` event. - * This event will automatically stop all media tracks and delete any HTML video element associated to this Publisher - * (only those videos [created by OpenVidu Browser](/en/stable/cheatsheet/manage-videos/#let-openvidu-take-care-of-the-video-players)). - * For every video removed, the Publisher object will dispatch a `videoElementDestroyed` event. - * Call `event.preventDefault()` upon event `streamDestroyed` if you want to clean the Publisher object on your own or re-publish it in a different Session. - * - * The {@link Session} object of every other participant connected to the session will dispatch a `streamDestroyed` event. - * This event will automatically unsubscribe the Subscriber object from the session (this includes closing the RTCPeerConnection and disposing all MediaStreamTracks) and - * delete any HTML video element associated to it (only those [created by OpenVidu Browser](/en/stable/cheatsheet/manage-videos/#let-openvidu-take-care-of-the-video-players)). - * For every video removed, the Subscriber object will dispatch a `videoElementDestroyed` event. - * Call `event.preventDefault()` upon event `streamDestroyed` to avoid this default behavior and take care of disposing and cleaning the Subscriber object on your own. - * - * See {@link StreamEvent} and {@link VideoElementEvent} to learn more. - */ - unpublish(publisher: Publisher): Promise { - return new Promise((resolve, reject) => { - if (!this.sessionConnected()) { - throw this.notConnectedError(); - } - - const stream = publisher.stream; - - if (!stream.connection) { - return reject(new Error('The associated Connection object of this Publisher is null')); - } else if (stream.connection !== this.connection) { - return reject( - new Error( - 'The associated Connection object of this Publisher is not your local Connection. ' + - "Only moderators can force unpublish on remote Streams via 'forceUnpublish' method" - ) - ); - } else { - logger.info('Unpublishing local media (' + stream.connection.connectionId + ')'); - - this.openvidu.sendRequest('unpublishVideo', (error, response) => { - if (error) { - return reject(error); - } else { - logger.info('Media unpublished correctly'); - - stream.disposeWebRtcPeer(); - - if (stream.connection.stream == stream) { - // The Connection.stream may have changed if Session.publish was called with other Publisher - delete stream.connection.stream; - } - - const streamEvent = new StreamEvent(true, publisher, 'streamDestroyed', publisher.stream, 'unpublish'); - publisher.emitEvent('streamDestroyed', [streamEvent]); - streamEvent.callDefaultBehavior(); - - return resolve(); - } - }); - } - }); - } - - /** - * Forces some user to leave the session - * - * #### Events dispatched - * - * The behavior is the same as when some user calls {@link Session.disconnect}, but `reason` property in all events will be `"forceDisconnectByUser"`. - * - * The {@link Session} object of every participant will dispatch a `streamDestroyed` event if the evicted user was publishing a stream, with property `reason` set to `"forceDisconnectByUser"`. - * The {@link Session} object of every participant except the evicted one will dispatch a `connectionDestroyed` event for the evicted user, with property `reason` set to `"forceDisconnectByUser"`. - * - * If any, the {@link Publisher} object of the evicted participant will also dispatch a `streamDestroyed` event with property `reason` set to `"forceDisconnectByUser"`. - * The {@link Session} object of the evicted participant will dispatch a `sessionDisconnected` event with property `reason` set to `"forceDisconnectByUser"`. - * - * See {@link StreamEvent}, {@link ConnectionEvent} and {@link SessionDisconnectedEvent} to learn more. - * - * @returns A Promise (to which you can optionally subscribe to) that is resolved only after the participant has been successfully evicted from the session and rejected with an Error object if not - */ - forceDisconnect(connection: Connection): Promise { - return new Promise((resolve, reject) => { - if (!this.sessionConnected()) { - return reject(this.notConnectedError()); - } - - logger.info('Forcing disconnect for connection ' + connection.connectionId); - this.openvidu.sendRequest('forceDisconnect', { connectionId: connection.connectionId }, (error, response) => { - if (error) { - logger.error('Error forcing disconnect for Connection ' + connection.connectionId, error); - if (error.code === 401) { - return reject( - new OpenViduError( - OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, - "You don't have permissions to force a disconnection" - ) - ); - } else { - return reject(error); - } - } else { - logger.info('Forcing disconnect correctly for Connection ' + connection.connectionId); - return resolve(); - } - }); - }); - } - - /** - * Forces some user to unpublish a Stream - * - * #### Events dispatched - * - * The behavior is the same as when some user calls {@link Session.unpublish}, but `reason` property in all events will be `"forceUnpublishByUser"` - * - * The {@link Session} object of every participant will dispatch a `streamDestroyed` event with property `reason` set to `"forceDisconnectByUser"` - * - * The {@link Publisher} object of the affected participant will also dispatch a `streamDestroyed` event with property `reason` set to `"forceDisconnectByUser"` - * - * See {@link StreamEvent} to learn more. - * - * @returns A Promise (to which you can optionally subscribe to) that is resolved only after the remote Stream has been successfully unpublished from the session and rejected with an Error object if not - */ - forceUnpublish(stream: Stream): Promise { - return new Promise((resolve, reject) => { - if (!this.sessionConnected()) { - return reject(this.notConnectedError()); - } - - logger.info('Forcing unpublish for stream ' + stream.streamId); - this.openvidu.sendRequest('forceUnpublish', { streamId: stream.streamId }, (error, response) => { - if (error) { - logger.error('Error forcing unpublish for Stream ' + stream.streamId, error); - if (error.code === 401) { - return reject( - new OpenViduError( - OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, - "You don't have permissions to force an unpublishing" - ) - ); - } else { - return reject(error); - } - } else { - logger.info('Forcing unpublish correctly for Stream ' + stream.streamId); - return resolve(); - } - }); - }); - } - - /** - * Sends one signal. `signal` object has the following optional properties: - * ```json - * {data:string, to:Connection[], type:string} - * ``` - * All users subscribed to that signal (`session.on('signal:type', ...)` or `session.on('signal', ...)` for all signals) and whose Connection objects are in `to` array will receive it. Their local - * Session objects will dispatch a `signal` or `signal:type` event. See {@link SignalEvent} to learn more. - * - * @returns A Promise (to which you can optionally subscribe to) that is resolved if the message successfully reached openvidu-server and rejected with an Error object if not. _This doesn't - * mean that openvidu-server could resend the message to all the listed receivers._ - */ - signal(signal: SignalOptions): Promise { - return new Promise((resolve, reject) => { - if (!this.sessionConnected()) { - return reject(this.notConnectedError()); - } - - const signalMessage = {}; - - if (signal.to && signal.to.length > 0) { - const connectionIds: string[] = []; - signal.to.forEach((connection) => { - if (!!connection.connectionId) { - connectionIds.push(connection.connectionId); - } - }); - signalMessage['to'] = connectionIds; - } else { - signalMessage['to'] = []; - } - - signalMessage['data'] = signal.data ? signal.data : ''; - - let typeAux: string = signal.type ? signal.type : 'signal'; - if (!!typeAux) { - if (typeAux.substring(0, 7) !== 'signal:') { - typeAux = 'signal:' + typeAux; - } - } - signalMessage['type'] = typeAux; - - this.openvidu.sendRequest( - 'sendMessage', - { - message: JSON.stringify(signalMessage) - }, - (error, response) => { - if (!!error) { - return reject(error); - } else { - return resolve(); - } - } - ); - }); - } - - /** - * Subscribe to the Speech-To-Text events for this {@link Stream}. The Session object will emit {@link SpeechToTextEvent} for the Stream - * when speech is detected in its audio track. - * - * @param stream - The Stream for which you want to start receiving {@link SpeechToTextEvent}. - * @param lang - The language of the Stream's audio track. It must be a valid [BCP-47](https://tools.ietf.org/html/bcp47) language tag like "en-US" or "es-ES". - * - * @returns A Promise (to which you can optionally subscribe to) that is resolved if the speech-to-text subscription - * was successful and rejected with an Error object if not. - */ - subscribeToSpeechToText(stream: Stream, lang: string): Promise { - return new Promise((resolve, reject) => { - this.openvidu.sendRequest( - 'subscribeToSpeechToText', - { - connectionId: stream.connection.connectionId, - lang - }, - (error, response) => { - if (!!error) { - return reject(error); - } else { - return resolve(); - } - } - ); - }); - } - - /** - * Unsubscribe from the Speech-To-Text events for this {@link Stream}. - * - * @returns A Promise (to which you can optionally subscribe to) that is resolved if the speech-to-text subscription - * was successful and rejected with an Error object if not. - */ - unsubscribeFromSpeechToText(stream: Stream): Promise { - return new Promise((resolve, reject) => { - this.openvidu.sendRequest( - 'unsubscribeFromSpeechToText', - { - connectionId: stream.connection.connectionId - }, - (error, response) => { - if (!!error) { - return reject(error); - } else { - return resolve(); - } - } - ); - }); - } - - /** - * See {@link EventDispatcher.on} - */ - on(type: K, handler: (event: SessionEventMap[K]) => void): this { - super.onAux(type, "Event '" + type + "' triggered by 'Session'", handler); - - if (type === 'publisherStartSpeaking') { - // If there are already available remote streams with audio, enable hark 'speaking' event in all of them - this.remoteConnections.forEach((remoteConnection) => { - if (!!remoteConnection.stream?.hasAudio) { - remoteConnection.stream.enableHarkSpeakingEvent(); - } - }); - if (!!this.connection?.stream?.hasAudio) { - // If connected to the Session and publishing with audio, also enable hark 'speaking' event for the Publisher - this.connection.stream.enableHarkSpeakingEvent(); - } - } - if (type === 'publisherStopSpeaking') { - // If there are already available remote streams with audio, enable hark 'stopped_speaking' event in all of them - this.remoteConnections.forEach((remoteConnection) => { - if (!!remoteConnection.stream?.hasAudio) { - remoteConnection.stream.enableHarkStoppedSpeakingEvent(); - } - }); - if (!!this.connection?.stream?.hasAudio) { - // If connected to the Session and publishing with audio, also enable hark 'stopped_speaking' event for the Publisher - this.connection.stream.enableHarkStoppedSpeakingEvent(); - } - } - - return this; - } - - /** - * See {@link EventDispatcher.once} - */ - once(type: K, handler: (event: SessionEventMap[K]) => void): this { - super.onceAux(type, "Event '" + type + "' triggered once by 'Session'", handler); - - if (type === 'publisherStartSpeaking') { - // If there are already available remote streams with audio, enable hark 'speaking' event (once) in all of them once - this.remoteConnections.forEach((remoteConnection) => { - if (!!remoteConnection.stream?.hasAudio) { - remoteConnection.stream.enableOnceHarkSpeakingEvent(); - } - }); - if (!!this.connection?.stream?.hasAudio) { - // If connected to the Session and publishing with audio, also enable hark 'speaking' event (once) for the Publisher - this.connection.stream.enableOnceHarkSpeakingEvent(); - } - } - if (type === 'publisherStopSpeaking') { - // If there are already available remote streams with audio, enable hark 'stopped_speaking' event (once) in all of them once - this.remoteConnections.forEach((remoteConnection) => { - if (!!remoteConnection.stream?.hasAudio) { - remoteConnection.stream.enableOnceHarkStoppedSpeakingEvent(); - } - }); - if (!!this.connection?.stream?.hasAudio) { - // If connected to the Session and publishing with audio, also enable hark 'stopped_speaking' event (once) for the Publisher - this.connection.stream.enableOnceHarkStoppedSpeakingEvent(); - } - } - - return this; - } - - /** - * See {@link EventDispatcher.off} - */ - off(type: K, handler?: (event: SessionEventMap[K]) => void): this { - super.offAux(type, handler); - - if (type === 'publisherStartSpeaking') { - // Check if Session object still has some listener for the event - if (!this.anySpeechEventListenerEnabled('publisherStartSpeaking', false)) { - this.remoteConnections.forEach((remoteConnection) => { - if (!!remoteConnection.stream?.streamManager) { - // Check if Subscriber object still has some listener for the event - if (!this.anySpeechEventListenerEnabled('publisherStartSpeaking', false, remoteConnection.stream.streamManager)) { - remoteConnection.stream.disableHarkSpeakingEvent(false); - } - } - }); - if (!!this.connection?.stream?.streamManager) { - // Check if Publisher object still has some listener for the event - if (!this.anySpeechEventListenerEnabled('publisherStartSpeaking', false, this.connection.stream.streamManager)) { - this.connection.stream.disableHarkSpeakingEvent(false); - } - } - } - } - if (type === 'publisherStopSpeaking') { - // Check if Session object still has some listener for the event - if (!this.anySpeechEventListenerEnabled('publisherStopSpeaking', false)) { - this.remoteConnections.forEach((remoteConnection) => { - if (!!remoteConnection.stream?.streamManager) { - // Check if Subscriber object still has some listener for the event - if (!this.anySpeechEventListenerEnabled('publisherStopSpeaking', false, remoteConnection.stream.streamManager)) { - remoteConnection.stream.disableHarkStoppedSpeakingEvent(false); - } - } - }); - if (!!this.connection?.stream?.streamManager) { - // Check if Publisher object still has some listener for the event - if (!this.anySpeechEventListenerEnabled('publisherStopSpeaking', false, this.connection.stream.streamManager)) { - this.connection.stream.disableHarkStoppedSpeakingEvent(false); - } - } - } - } - return this; - } - - /* Hidden methods */ - - /** - * @hidden - */ - onParticipantJoined(event: RemoteConnectionOptions): void { - // Connection shouldn't exist - this.getConnection(event.id, '') - .then((connection) => { - logger.warn('Connection ' + connection.connectionId + ' already exists in connections list'); - }) - .catch((openViduError) => { - const connection = new Connection(this, event); - this.remoteConnections.set(event.id, connection); - this.ee.emitEvent('connectionCreated', [new ConnectionEvent(false, this, 'connectionCreated', connection, '')]); - }); - } - - /** - * @hidden - */ - onParticipantLeft(event: { connectionId: string; reason: ConnectionEventReason }): void { - this.getRemoteConnection(event.connectionId, 'onParticipantLeft') - .then((connection) => { - if (!!connection.stream) { - const stream = connection.stream; - - const streamEvent = new StreamEvent(true, this, 'streamDestroyed', stream, event.reason); - this.ee.emitEvent('streamDestroyed', [streamEvent]); - streamEvent.callDefaultBehavior(); - - this.remoteStreamsCreated.delete(stream.streamId); - } - connection.dispose(); - this.remoteConnections.delete(connection.connectionId); - this.ee.emitEvent('connectionDestroyed', [ - new ConnectionEvent(false, this, 'connectionDestroyed', connection, event.reason) - ]); - }) - .catch((openViduError) => { - logger.error(openViduError); - }); - } - - /** - * @hidden - */ - onParticipantPublished(event: RemoteConnectionOptions): void { - const afterConnectionFound = (connection) => { - this.remoteConnections.set(connection.connectionId, connection); - - if (!this.remoteStreamsCreated.get(connection.stream.streamId)) { - // Avoid race condition between stream.subscribe() in "onParticipantPublished" and in "joinRoom" rpc callback - // This condition is false if openvidu-server sends "participantPublished" event to a subscriber participant that has - // already subscribed to certain stream in the callback of "joinRoom" method - - this.ee.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', connection.stream, '')]); - } - - this.remoteStreamsCreated.set(connection.stream.streamId, true); - }; - - // Get the existing Connection created on 'onParticipantJoined' for - // existing participants or create a new one for new participants - let connection: Connection; - this.getRemoteConnection(event.id, 'onParticipantPublished') - - .then((con) => { - // Update existing Connection - connection = con; - event.metadata = con.data; - connection.remoteOptions = event; - connection.initRemoteStreams(event.streams); - afterConnectionFound(connection); - }) - .catch((openViduError) => { - // Create new Connection - connection = new Connection(this, event); - afterConnectionFound(connection); - }); - } - - /** - * @hidden - */ - onParticipantUnpublished(event: { connectionId: string; reason: StreamEventReason }): void { - if (event.connectionId === this.connection.connectionId) { - // Your stream has been forcedly unpublished from the session - this.stopPublisherStream(event.reason); - } else { - this.getRemoteConnection(event.connectionId, 'onParticipantUnpublished') - - .then((connection) => { - const streamEvent = new StreamEvent(true, this, 'streamDestroyed', connection.stream!, event.reason); - this.ee.emitEvent('streamDestroyed', [streamEvent]); - streamEvent.callDefaultBehavior(); - - // Deleting the remote stream if it exists - if (connection.stream != null) { - const streamId: string = connection.stream!.streamId; - this.remoteStreamsCreated.delete(streamId); - connection.removeStream(); - } - }) - .catch((openViduError) => { - logger.error(openViduError); - }); - } - } - - /** - * @hidden - */ - onParticipantEvicted(event: { connectionId: string; reason: ConnectionEventReason }): void { - if (event.connectionId === this.connection.connectionId) { - // You have been evicted from the session - if (!!this.sessionId && !this.connection.disposed) { - this.leave(true, event.reason); - } - } - } - - /** - * @hidden - */ - onNewMessage(event: { type?: string; data?: string; from?: string }): void { - logger.info('New signal: ' + JSON.stringify(event)); - - const strippedType = !!event.type ? event.type.replace(/^(signal:)/, '') : undefined; - - if (!!event.from) { - // Signal sent by other client - this.getConnection( - event.from, - "Connection '" + - event.from + - "' unknown when 'onNewMessage'. Existing remote connections: " + - JSON.stringify(this.remoteConnections.keys()) + - '. Existing local connection: ' + - this.connection.connectionId - ) - - .then((connection) => { - this.ee.emitEvent('signal', [new SignalEvent(this, strippedType, event.data, connection)]); - if (!!event.type && event.type !== 'signal') { - this.ee.emitEvent(event.type, [new SignalEvent(this, strippedType, event.data, connection)]); - } - }) - .catch((openViduError) => { - logger.error(openViduError); - }); - } else { - // Signal sent by server - this.ee.emitEvent('signal', [new SignalEvent(this, strippedType, event.data, undefined)]); - if (!!event.type && event.type !== 'signal') { - this.ee.emitEvent(event.type, [new SignalEvent(this, strippedType, event.data, undefined)]); - } - } - } - - /** - * @hidden - */ - onStreamPropertyChanged(event: { connectionId: string; streamId: string; property: ChangedPropertyType; newValue: any; reason: StreamPropertyChangedEventReason }): void { - const callback = (connection: Connection) => { - if (!!connection.stream && connection.stream.streamId === event.streamId) { - const stream = connection.stream; - let oldValue; - switch (event.property) { - case 'audioActive': - oldValue = stream.audioActive; - event.newValue = event.newValue === 'true'; - stream.audioActive = event.newValue; - break; - case 'videoActive': - oldValue = stream.videoActive; - event.newValue = event.newValue === 'true'; - stream.videoActive = event.newValue; - const videoTrack = stream?.getMediaStream()?.getVideoTracks()?.[0]; - if(videoTrack && !videoTrack.enabled && stream.videoActive){ - videoTrack.enabled = true; - } - break; - case 'videoTrack': - event.newValue = JSON.parse(event.newValue); - break; - case 'audioTrack': - event.newValue = JSON.parse(event.newValue); - break; - case 'videoDimensions': - oldValue = stream.videoDimensions; - event.newValue = JSON.parse(JSON.parse(event.newValue)); - stream.videoDimensions = event.newValue; - break; - case 'filter': - oldValue = stream.filter; - event.newValue = Object.keys(event.newValue).length > 0 ? event.newValue : undefined; - if (event.newValue !== undefined) { - stream.filter = new Filter(event.newValue.type, event.newValue.options); - stream.filter.stream = stream; - if (event.newValue.lastExecMethod) { - stream.filter.lastExecMethod = event.newValue.lastExecMethod; - } - } else { - delete stream.filter; - } - event.newValue = stream.filter; - break; - } - this.ee.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent(this, stream, event.property, event.newValue, oldValue, event.reason) - ]); - if (!!stream.streamManager) { - stream.streamManager.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent(stream.streamManager, stream, event.property, event.newValue, oldValue, event.reason) - ]); - } - } else { - logger.error( - "No stream with streamId '" + - event.streamId + - "' found for connection '" + - event.connectionId + - "' on 'streamPropertyChanged' event" - ); - } - }; - - if (event.connectionId === this.connection.connectionId) { - // Your stream has been forcedly changed (filter feature) - callback(this.connection); - } else { - this.getRemoteConnection(event.connectionId, 'onStreamPropertyChanged') - .then((connection) => { - callback(connection); - }) - .catch((openViduError) => { - logger.error(openViduError); - }); - } - } - - /** - * @hidden - */ - onConnectionPropertyChanged(event: { property: string; newValue: any }): void { - let oldValue; - switch (event.property) { - case 'role': - oldValue = this.connection.role.slice(); - this.connection.role = event.newValue; - this.connection.localOptions!.role = event.newValue; - break; - case 'record': - oldValue = this.connection.record; - event.newValue = event.newValue === 'true'; - this.connection.record = event.newValue; - this.connection.localOptions!.record = event.newValue; - break; - } - this.ee.emitEvent('connectionPropertyChanged', [ - new ConnectionPropertyChangedEvent(this, this.connection, event.property, event.newValue, oldValue) - ]); - } - - /** - * @hidden - */ - onNetworkQualityLevelChangedChanged(event: { connectionId: string; newValue: number; oldValue: number }): void { - if (event.connectionId === this.connection.connectionId) { - this.ee.emitEvent('networkQualityLevelChanged', [ - new NetworkQualityLevelChangedEvent(this, event.newValue, event.oldValue, this.connection) - ]); - } else { - this.getConnection(event.connectionId, 'Connection not found for connectionId ' + event.connectionId) - .then((connection: Connection) => { - this.ee.emitEvent('networkQualityLevelChanged', [ - new NetworkQualityLevelChangedEvent(this, event.newValue, event.oldValue, connection) - ]); - }) - .catch((openViduError) => { - logger.error(openViduError); - }); - } - } - - /** - * @hidden - */ - recvIceCandidate(event: { - senderConnectionId: string; - endpointName: string; - sdpMLineIndex: number; - sdpMid: string; - candidate: string; - }): void { - // The event contains fields that can be used to obtain a proper candidate, - // using the RTCIceCandidate constructor: - // https://w3c.github.io/webrtc-pc/#dom-rtcicecandidate-constructor - const candidateInit: RTCIceCandidateInit = { - candidate: event.candidate, - sdpMLineIndex: event.sdpMLineIndex, - sdpMid: event.sdpMid - }; - const iceCandidate = new RTCIceCandidate(candidateInit); - - this.getConnection( - event.senderConnectionId, - 'Connection not found for connectionId ' + - event.senderConnectionId + - ' owning endpoint ' + - event.endpointName + - '. Ice candidate will be ignored: ' + - iceCandidate - ) - .then((connection) => { - const stream: Stream = connection.stream!; - stream - .getWebRtcPeer() - .addIceCandidate(iceCandidate) - .catch((error) => { - logger.error( - 'Error adding candidate for ' + stream!.streamId + ' stream of endpoint ' + event.endpointName + ': ' + error - ); - }); - }) - .catch((openViduError) => { - logger.error(openViduError); - }); - } - - /** - * @hidden - */ - onSessionClosed(msg): void { - logger.info('Session closed: ' + JSON.stringify(msg)); - const s = msg.sessionId; - if (s !== undefined) { - this.ee.emitEvent('session-closed', [ - { - session: s - } - ]); - } else { - logger.warn('Session undefined on session closed', msg); - } - } - - /** - * @hidden - */ - onLostConnection(reason: ConnectionEventReason): void { - logger.warn('Lost connection in Session ' + this.sessionId); - if (!!this.sessionId && !!this.connection && !this.connection.disposed) { - this.leave(true, reason); - } - } - - /** - * @hidden - */ - onRecoveredConnection(): void { - logger.info('Recovered connection in Session ' + this.sessionId); - this.reconnectBrokenStreams(); - this.ee.emitEvent('reconnected', []); - } - - /** - * @hidden - */ - onMediaError(event: { error: string }): void { - logger.error('Media error: ' + JSON.stringify(event)); - const err = event.error; - if (err) { - this.ee.emitEvent('error-media', [{ error: err }]); - } else { - logger.warn('Received undefined media error:', event); - } - } - - /** - * @hidden - */ - onRecordingStarted(event: { id: string; name: string }): void { - this.ee.emitEvent('recordingStarted', [new RecordingEvent(this, 'recordingStarted', event.id, event.name)]); - } - - /** - * @hidden - */ - onRecordingStopped(event: { id: string; name: string; reason: RecordingEventReason }): void { - this.ee.emitEvent('recordingStopped', [new RecordingEvent(this, 'recordingStopped', event.id, event.name, event.reason)]); - } - - /** - * @hidden - */ - onBroadcastStarted(): void { - this.ee.emitEvent('broadcastStarted', []); - } - - /** - * @hidden - */ - onBroadcastStopped(): void { - this.ee.emitEvent('broadcastStopped', []); - } - - /** - * @hidden - */ - onFilterEventDispatched(event: { connectionId: string; streamId: string; filterType: string; eventType: string; data: string }): void { - const connectionId: string = event.connectionId; - this.getConnection(connectionId, 'No connection found for connectionId ' + connectionId).then((connection) => { - logger.info(`Filter event of type "${event.eventType}" dispatched`); - const stream: Stream = connection.stream!; - if (!stream || !stream.filter) { - return logger.error( - `Filter event of type "${event.eventType}" dispatched for stream ${stream.streamId} but there is no ${!stream ? 'stream' : 'filter' - } defined` - ); - } - const eventHandler = stream.filter.handlers.get(event.eventType); - if (!eventHandler || typeof eventHandler !== 'function') { - const actualHandlers: string[] = Array.from(stream.filter.handlers.keys()); - return logger.error( - `Filter event of type "${event.eventType}" not handled or not a function! Active filter events: ${actualHandlers.join( - ',' - )}` - ); - } else { - eventHandler.call(this, new FilterEvent(stream.filter, event.eventType, event.data)); - } - }); - } - - /** - * @hidden - */ - onForciblyReconnectSubscriber(event: { connectionId: string; streamId: string; sdpOffer: string }): Promise { - return new Promise((resolve, reject) => { - this.getRemoteConnection(event.connectionId, 'onForciblyReconnectSubscriber') - .then((connection) => { - if (!!connection.stream && connection.stream.streamId === event.streamId) { - const stream = connection.stream; - - if (stream.setupReconnectionEventEmitter(resolve, reject)) { - // Ongoing reconnection - // Wait for the event emitter to be free (with success or error) and call the method again - if (stream.reconnectionEventEmitter!['onForciblyReconnectSubscriberLastEvent'] != null) { - // Two or more onForciblyReconnectSubscriber events were received while a reconnection process - // of the subscriber was already taking place. Always use the last one to retry the re-subscription - // process, as that SDP offer will be the only one available at the server side. Ignore previous ones - stream.reconnectionEventEmitter!['onForciblyReconnectSubscriberLastEvent'] = event; - return reject('Ongoing forced subscriber reconnection'); - } else { - // One onForciblyReconnectSubscriber even has been received while a reconnection process - // of the subscriber was already taking place. Set up a listener to wait for it to retry the - // forced reconnection process - stream.reconnectionEventEmitter!['onForciblyReconnectSubscriberLastEvent'] = event; - const callback = () => { - const eventAux = stream.reconnectionEventEmitter!['onForciblyReconnectSubscriberLastEvent']; - delete stream.reconnectionEventEmitter!['onForciblyReconnectSubscriberLastEvent']; - this.onForciblyReconnectSubscriber(eventAux); - }; - stream.reconnectionEventEmitter!.once('success', () => { - callback(); - }); - stream.reconnectionEventEmitter!.once('error', () => { - callback(); - }); - } - return; - } - - stream - .completeWebRtcPeerReceive(true, true, event.sdpOffer) - .then(() => stream.finalResolveForSubscription(true, resolve)) - .catch((error) => - stream.finalRejectForSubscription( - true, - `Error while forcibly reconnecting remote stream ${event.streamId}: ${error.toString()}`, - reject - ) - ); - } else { - const errMsg = - "No stream with streamId '" + - event.streamId + - "' found for connection '" + - event.connectionId + - "' on 'streamPropertyChanged' event"; - logger.error(errMsg); - return reject(errMsg); - } - }) - .catch((openViduError) => { - logger.error(openViduError); - return reject(openViduError); - }); - }); - } - - /** - * @hidden - */ - reconnectBrokenStreams(): void { - logger.info('Re-establishing media connections...'); - let someReconnection = false; - // Re-establish Publisher stream - if (!!this.connection.stream && this.connection.stream.streamIceConnectionStateBroken()) { - logger.warn('Re-establishing Publisher ' + this.connection.stream.streamId); - this.connection.stream.initWebRtcPeerSend(true); - someReconnection = true; - } - // Re-establish Subscriber streams - this.remoteConnections.forEach((remoteConnection) => { - if (!!remoteConnection.stream && remoteConnection.stream.streamIceConnectionStateBroken()) { - logger.warn('Re-establishing Subscriber ' + remoteConnection.stream.streamId); - remoteConnection.stream.initWebRtcPeerReceive(true); - someReconnection = true; - } - }); - if (!someReconnection) { - logger.info('There were no media streams in need of a reconnection'); - } - } - - /** - * @hidden - */ - async onSpeechToTextMessage(event: { - timestamp?: Date; - streamId: string; - connectionId: string; - sessionId: string; - text: string; - reason: string; - raw: string; - lang: string; - }): Promise { - const connection = await this.getConnection(event.connectionId, 'No connection found for connectionId ' + event.connectionId); - const ev = new SpeechToTextEvent(this, connection, event.text, (event.reason).toLowerCase(), event.raw, event.lang); - this.ee.emitEvent('speechToTextMessage', [ev]); - } - - /** - * @hidden - */ - async onSpeechToTextDisconnected(event: { message: string }): Promise { - this.emitEvent('exception', [new ExceptionEvent(this, ExceptionEventName.SPEECH_TO_TEXT_DISCONNECTED, this, event.message)]); - } - - /** - * @hidden - */ - emitEvent(type: string, eventArray: any[]): void { - this.ee.emitEvent(type, eventArray); - } - - /** - * @hidden - */ - leave(forced: boolean, reason: ConnectionEventReason): void { - forced = !!forced; - logger.info('Leaving Session (forced=' + forced + ')'); - this.stopVideoDataIntervals(); - - if (!!this.connection) { - if (!this.connection.disposed && !forced) { - this.openvidu.sendRequest('leaveRoom', (error, response) => { - if (error) { - logger.error(`leaveRoom error: ${JSON.stringify(error)}`); - } - this.openvidu.closeWs(); - }); - } else { - this.openvidu.closeWs(); - } - - this.stopPublisherStream(reason); - - if (!this.connection.disposed) { - // Make Session object dispatch 'sessionDisconnected' event (if it is not already disposed) - const sessionDisconnectEvent = new SessionDisconnectedEvent(this, reason); - this.ee.emitEvent('sessionDisconnected', [sessionDisconnectEvent]); - sessionDisconnectEvent.callDefaultBehavior(); - } - } else { - logger.warn('You were not connected to the session ' + this.sessionId); - } - logger.flush(); - } - - /** - * @hidden - */ - initializeParams(token: string) { - const joinParams = { - token: !!token ? token : '', - session: this.sessionId, - platform: !!platform.getDescription() ? platform.getDescription() : 'unknown', - sdkVersion: this.openvidu.libraryVersion, - metadata: !!this.options.metadata ? this.options.metadata : '', - secret: this.openvidu.getSecret(), - recorder: this.openvidu.getRecorder(), - stt: this.openvidu.getStt() - }; - return joinParams; - } - - /** - * @hidden - */ - sendVideoData(streamManager: StreamManager, intervalSeconds: number = 1, doInterval: boolean = false, maxLoops: number = 1) { - if ( - platform.isChromeBrowser() || - platform.isChromeMobileBrowser() || - platform.isOperaBrowser() || - platform.isOperaMobileBrowser() || - platform.isEdgeBrowser() || - platform.isEdgeMobileBrowser() || - platform.isElectron() || - (platform.isSafariBrowser() && !platform.isIonicIos()) || - platform.isAndroidBrowser() || - platform.isSamsungBrowser() || - platform.isIonicAndroid() || - platform.isIOSWithSafari() - ) { - const obtainAndSendVideo = async () => { - const pc = streamManager.stream.getRTCPeerConnection(); - if (pc.connectionState === 'connected') { - const statsMap = await pc.getStats(); - const arr: any[] = []; - statsMap.forEach((stats) => { - if ('frameWidth' in stats && 'frameHeight' in stats && arr.length === 0) { - arr.push(stats); - } - }); - if (arr.length > 0) { - this.openvidu.sendRequest( - 'videoData', - { - height: arr[0].frameHeight, - width: arr[0].frameWidth, - videoActive: streamManager.stream.videoActive != null ? streamManager.stream.videoActive : false, - audioActive: streamManager.stream.audioActive != null ? streamManager.stream.audioActive : false - }, - (error, response) => { - if (error) { - logger.error("Error sending 'videoData' event", error); - } - } - ); - } - } - }; - if (doInterval) { - let loops = 1; - this.videoDataInterval = setInterval(() => { - if (loops < maxLoops) { - loops++; - obtainAndSendVideo(); - } else { - clearInterval(this.videoDataInterval); - } - }, intervalSeconds * 1000); - } else { - this.videoDataTimeout = setTimeout(obtainAndSendVideo, intervalSeconds * 1000); - } - } else if (platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser() || platform.isIonicIos() || platform.isReactNative()) { - // Basic version for Firefox and Ionic iOS. They do not support stats - this.openvidu.sendRequest( - 'videoData', - { - height: streamManager.stream.videoDimensions?.height || 0, - width: streamManager.stream.videoDimensions?.width || 0, - videoActive: streamManager.stream.videoActive != null ? streamManager.stream.videoActive : false, - audioActive: streamManager.stream.audioActive != null ? streamManager.stream.audioActive : false - }, - (error, response) => { - if (error) { - logger.error("Error sending 'videoData' event", error); - } - } - ); - } else { - logger.error( - 'Browser ' + - platform.getName() + - ' (version ' + - platform.getVersion() + - ') for ' + - platform.getFamily() + - ' is not supported in OpenVidu for Network Quality' - ); - } - } - - /** - * @hidden - */ - sessionConnected() { - return this.connection != null; - } - - /** - * @hidden - */ - notConnectedError(): OpenViduError { - return new OpenViduError( - OpenViduErrorName.OPENVIDU_NOT_CONNECTED, - "There is no connection to the session. Method 'Session.connect' must be successfully completed first" - ); - } - - /** - * @hidden - */ - anySpeechEventListenerEnabled(event: string, onlyOnce: boolean, streamManager?: StreamManager): boolean { - let handlersInSession = this.ee.getListeners(event); - if (onlyOnce) { - handlersInSession = handlersInSession.filter((h) => (h as any).once); - } - let listenersInSession = handlersInSession.length; - if (listenersInSession > 0) return true; - let listenersInStreamManager = 0; - if (!!streamManager) { - let handlersInStreamManager = streamManager.ee.getListeners(event); - if (onlyOnce) { - handlersInStreamManager = handlersInStreamManager.filter((h) => (h as any).once); - } - listenersInStreamManager = handlersInStreamManager.length; - } - return listenersInStreamManager > 0; - } - - /** - * @hidden - */ - getTokenParams(token: string) { - const match = token.match(/^(wss?)\:\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([\/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/); - if (!!match) { - const url = { - protocol: match[1], - host: match[2], - hostname: match[3], - port: match[4], - pathname: match[5], - search: match[6], - hash: match[7] - }; - - const params = token.split('?'); - const queryParams = decodeURI(params[1]) - .split('&') - .map((param) => param.split('=')) - .reduce((values, [key, value]) => { - values[key] = value; - return values; - }, {}); - - return { - sessionId: queryParams['sessionId'], - secret: queryParams['secret'], - recorder: queryParams['recorder'], - stt: queryParams['stt'], - webrtcStatsInterval: queryParams['webrtcStatsInterval'], - sendBrowserLogs: queryParams['sendBrowserLogs'], - edition: queryParams['edition'], - wsUri: url.protocol + '://' + url.host + '/openvidu', - httpUri: 'https://' + url.host - }; - } else { - throw new Error(`Token not valid: "${token}"`); - } - } - - /* Private methods */ - - private connectAux(token: string): Promise { - return new Promise((resolve, reject) => { - this.openvidu.startWs((error) => { - if (!!error) { - return reject(error); - } else { - const joinParams = this.initializeParams(token); - - this.openvidu.sendRequest('joinRoom', joinParams, (error, response: LocalConnectionOptions) => { - if (!!error) { - return reject(error); - } else { - // Process join room response - this.processJoinRoomResponse(response, token); - - // Initialize local Connection object with values returned by openvidu-server - this.connection = new Connection(this, response); - - // Initialize remote Connections with value returned by openvidu-server - const events = { - connections: new Array(), - streams: new Array() - }; - const existingParticipants: RemoteConnectionOptions[] = response.value; - existingParticipants.forEach((remoteConnectionOptions: RemoteConnectionOptions) => { - const connection = new Connection(this, remoteConnectionOptions); - this.remoteConnections.set(connection.connectionId, connection); - events.connections.push(connection); - if (!!connection.stream) { - this.remoteStreamsCreated.set(connection.stream.streamId, true); - events.streams.push(connection.stream); - } - }); - - // Own 'connectionCreated' event - this.ee.emitEvent('connectionCreated', [ - new ConnectionEvent(false, this, 'connectionCreated', this.connection, '') - ]); - - // One 'connectionCreated' event for each existing connection in the session - events.connections.forEach((connection) => { - this.ee.emitEvent('connectionCreated', [ - new ConnectionEvent(false, this, 'connectionCreated', connection, '') - ]); - }); - - // One 'streamCreated' event for each active stream in the session - events.streams.forEach((stream) => { - this.ee.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', stream, '')]); - }); - - if (!!response.recordingId && !!response.recordingName) { - this.ee.emitEvent('recordingStarted', [ - new RecordingEvent(this, 'recordingStarted', response.recordingId, response.recordingName) - ]); - } - - return resolve(); - } - }); - } - }); - }); - } - - private stopPublisherStream(reason: StreamEventReason) { - if (!!this.connection.stream) { - // Dispose Publisher's local stream - this.connection.stream.disposeWebRtcPeer(); - if (this.connection.stream.isLocalStreamPublished) { - // Make Publisher object dispatch 'streamDestroyed' event if the Stream was published - this.connection.stream.ee.emitEvent('local-stream-destroyed', [reason]); - } - } - } - - private stopVideoDataIntervals(): void { - clearInterval(this.videoDataInterval); - clearTimeout(this.videoDataTimeout); - } - - private stringClientMetadata(metadata: any): string { - if (typeof metadata !== 'string') { - return JSON.stringify(metadata); - } else { - return metadata; - } - } - - protected getConnection(connectionId: string, errorMessage: string): Promise { - return new Promise((resolve, reject) => { - const connection = this.remoteConnections.get(connectionId); - if (!!connection) { - // Resolve remote connection - return resolve(connection); - } else { - if (this.connection.connectionId === connectionId) { - // Resolve local connection - return resolve(this.connection); - } else { - // Connection not found. Reject with OpenViduError - return reject(new OpenViduError(OpenViduErrorName.GENERIC_ERROR, errorMessage)); - } - } - }); - } - - private getRemoteConnection(connectionId: string, operation: string): Promise { - return new Promise((resolve, reject) => { - const connection = this.remoteConnections.get(connectionId); - if (!!connection) { - // Resolve remote connection - return resolve(connection); - } else { - // Remote connection not found. Reject with OpenViduError - const errorMessage = - 'Remote connection ' + - connectionId + - " unknown when '" + - operation + - "'. " + - 'Existing remote connections: ' + - JSON.stringify(this.remoteConnections.keys()); - return reject(new OpenViduError(OpenViduErrorName.GENERIC_ERROR, errorMessage)); - } - }); - } - - private processToken(token: string): void { - const tokenParams = this.getTokenParams(token); - this.sessionId = tokenParams.sessionId; - - if (!!tokenParams.secret) { - this.openvidu.secret = tokenParams.secret; - } - if (!!tokenParams.recorder) { - this.openvidu.recorder = true; - } - if (!!tokenParams.stt) { - this.openvidu.stt = true; - } - if (!!tokenParams.webrtcStatsInterval) { - this.openvidu.webrtcStatsInterval = tokenParams.webrtcStatsInterval; - } - if (!!tokenParams.sendBrowserLogs) { - this.openvidu.sendBrowserLogs = tokenParams.sendBrowserLogs; - } - this.openvidu.isAtLeastPro = tokenParams.edition === 'pro' || tokenParams.edition === 'enterprise'; - this.openvidu.isEnterprise = tokenParams.edition === 'enterprise'; - - this.openvidu.wsUri = tokenParams.wsUri; - this.openvidu.httpUri = tokenParams.httpUri; - } - - private processJoinRoomResponse(opts: LocalConnectionOptions, token: string) { - this.sessionId = opts.session; - if (opts.customIceServers != null && opts.customIceServers.length > 0) { - this.openvidu.iceServers = []; - for (const iceServer of opts.customIceServers) { - let rtcIceServer: RTCIceServer = { - urls: [iceServer.url] - }; - logger.log('STUN/TURN server IP: ' + iceServer.url); - if (iceServer.username != null && iceServer.credential != null) { - rtcIceServer.username = iceServer.username; - rtcIceServer.credential = iceServer.credential; - logger.log('TURN credentials [' + iceServer.username + ':' + iceServer.credential + ']'); - } - this.openvidu.iceServers.push(rtcIceServer); - } - } - this.openvidu.role = opts.role; - this.openvidu.finalUserId = opts.finalUserId; - this.openvidu.mediaServer = opts.mediaServer; - this.openvidu.videoSimulcast = opts.videoSimulcast; - this.capabilities = { - subscribe: true, - publish: this.openvidu.role !== 'SUBSCRIBER', - forceUnpublish: this.openvidu.role === 'MODERATOR', - forceDisconnect: this.openvidu.role === 'MODERATOR' - }; - logger.info('openvidu-server version: ' + opts.version); - if (opts.life != null) { - this.openvidu.life = opts.life; - } - const minorDifference: number = semverMinor(opts.version) - semverMinor(this.openvidu.libraryVersion); - if (semverMajor(opts.version) !== semverMajor(this.openvidu.libraryVersion) || !(minorDifference == 0 || minorDifference == 1)) { - logger.error( - `openvidu-browser (${this.openvidu.libraryVersion}) and openvidu-server (${opts.version}) versions are incompatible. ` + - 'Errors are likely to occur. openvidu-browser SDK is only compatible with the same version or the immediately following minor version of an OpenVidu deployment' - ); - } else if (minorDifference == 1) { - logger.warn( - `openvidu-browser version ${this.openvidu.libraryVersion} does not match openvidu-server version ${opts.version}. ` + - `These versions are still compatible with each other, but openvidu-browser version must be updated as soon as possible to ${semverMajor( - opts.version - )}.${semverMinor(opts.version)}.x. ` + - `This client using openvidu-browser ${this.openvidu.libraryVersion} will become incompatible with the next release of openvidu-server` - ); - } - - // Configure JSNLogs - OpenViduLogger.configureJSNLog(this.openvidu, token); - - // Store token - this.token = token; - } -} diff --git a/openvidu-browser/src/OpenVidu/Stream.ts b/openvidu-browser/src/OpenVidu/Stream.ts deleted file mode 100644 index cb228f50..00000000 --- a/openvidu-browser/src/OpenVidu/Stream.ts +++ /dev/null @@ -1,1877 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Connection } from './Connection'; -import { Filter } from './Filter'; -import { Publisher } from './Publisher'; -import { Session } from './Session'; -import { StreamManager } from './StreamManager'; -import { Subscriber } from './Subscriber'; -import { InboundStreamOptions } from '../OpenViduInternal/Interfaces/Private/InboundStreamOptions'; -import { OutboundStreamOptions } from '../OpenViduInternal/Interfaces/Private/OutboundStreamOptions'; -import { - WebRtcPeer, - WebRtcPeerSendonly, - WebRtcPeerRecvonly, - WebRtcPeerSendrecv, - WebRtcPeerConfiguration -} from '../OpenViduInternal/WebRtcPeer/WebRtcPeer'; -import { WebRtcStats } from '../OpenViduInternal/WebRtcStats/WebRtcStats'; -import { ExceptionEvent, ExceptionEventName } from '../OpenViduInternal/Events/ExceptionEvent'; -import { PublisherSpeakingEvent } from '../OpenViduInternal/Events/PublisherSpeakingEvent'; -import { StreamManagerEvent } from '../OpenViduInternal/Events/StreamManagerEvent'; -import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent'; -import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError'; -import { TypeOfVideo } from '../OpenViduInternal/Enums/TypeOfVideo'; -import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; -import { PlatformUtils } from '../OpenViduInternal/Utils/Platform'; - -import { v4 as uuidv4 } from 'uuid'; - -/** - * @hidden - */ -import hark = require('hark'); -/** - * @hidden - */ -import EventEmitter = require('wolfy87-eventemitter'); -/** - * @hidden - */ -const logger: OpenViduLogger = OpenViduLogger.getInstance(); - -/** - * @hidden - */ -let platform: PlatformUtils; - -/** - * Represents each one of the media streams available in OpenVidu Server for certain session. - * Each {@link Publisher} and {@link Subscriber} has an attribute of type Stream, as they give access - * to one of them (sending and receiving it, respectively) - */ -export class Stream { - /** - * The Connection object that is publishing the stream - */ - connection: Connection; - - /** - * Frame rate of the video in frames per second. This property is only defined if the {@link Publisher} of - * the stream was initialized passing a _frameRate_ property on {@link OpenVidu.initPublisher} method - */ - frameRate?: number; - - /** - * Whether the stream has a video track or not - */ - hasVideo: boolean; - - /** - * Whether the stream has an audio track or not - */ - hasAudio: boolean; - - /** - * Whether the stream has the video track muted or unmuted. If {@link hasVideo} is false, this property is undefined. - * - * This property may change if the Publisher publishing the stream calls {@link Publisher.publishVideo}. Whenever this happens a {@link StreamPropertyChangedEvent} will be dispatched - * by the Session object as well as by the affected Subscriber/Publisher object - */ - videoActive: boolean; - - /** - * Whether the stream has the audio track muted or unmuted. If {@link hasAudio} is false, this property is undefined - * - * This property may change if the Publisher publishing the stream calls {@link Publisher.publishAudio}. Whenever this happens a {@link StreamPropertyChangedEvent} will be dispatched - * by the Session object as well as by the affected Subscriber/Publisher object - */ - audioActive: boolean; - - /** - * Unique identifier of the stream. If the stream belongs to a... - * - Subscriber object: property `streamId` is always defined - * - Publisher object: property `streamId` is only defined after successful execution of {@link Session.publish} - */ - streamId: string; - - /** - * Time when this stream was created in OpenVidu Server (UTC milliseconds). Depending on the owner of this stream: - * - Subscriber object: property `creationTime` is always defined - * - Publisher object: property `creationTime` is only defined after successful execution of {@link Session.publish} - */ - creationTime: number; - - /** - * Can be: - * - `"CAMERA"`: when the video source comes from a webcam. - * - `"SCREEN"`: when the video source comes from screen-sharing. - * - `"CUSTOM"`: when {@link PublisherProperties.videoSource} has been initialized in the Publisher side with a custom MediaStreamTrack when calling {@link OpenVidu.initPublisher}). - * - `"IPCAM"`: when the video source comes from an IP camera participant instead of a regular participant (see [IP cameras](/en/stable/advanced-features/ip-cameras/)). - * - * If {@link hasVideo} is false, this property is undefined - */ - typeOfVideo?: keyof typeof TypeOfVideo; // TODO: Change this type to enum TypeOfVideo on the next breaking-change release - - /** - * StreamManager object ({@link Publisher} or {@link Subscriber}) in charge of displaying this stream in the DOM - */ - streamManager: StreamManager; - - /** - * Width and height in pixels of the encoded video stream. If {@link hasVideo} is false, this property is undefined - * - * This property may change if the Publisher that is publishing: - * - If it is a mobile device, whenever the user rotates the device. - * - If it is screen-sharing, whenever the user changes the size of the captured window. - * - * Whenever this happens a {@link StreamPropertyChangedEvent} will be dispatched by the Session object as well as by the affected Subscriber/Publisher object - */ - videoDimensions: { width: number; height: number }; - - /** - * **WARNING**: experimental option. This interface may change in the near future - * - * Filter applied to the Stream. You can apply filters by calling {@link Stream.applyFilter}, execute methods of the applied filter with - * {@link Filter.execMethod} and remove it with {@link Stream.removeFilter}. Be aware that the client calling this methods must have the - * necessary permissions: the token owned by the client must have been initialized with the appropriated `allowedFilters` array. - */ - filter?: Filter; - - protected webRtcPeer: WebRtcPeer; - protected mediaStream?: MediaStream; - private webRtcStats: WebRtcStats; - - private isSubscribeToRemote = false; - - private virtualBackgroundSourceElements?: { videoClone: HTMLVideoElement; mediaStreamClone: MediaStream }; - /** - * @hidden - */ - virtualBackgroundSinkElements?: { VB: any; video: HTMLVideoElement }; - - /** - * @hidden - */ - isLocalStreamReadyToPublish = false; - /** - * @hidden - */ - isLocalStreamPublished = false; - /** - * @hidden - */ - publishedOnce = false; - /** - * @hidden - */ - session: Session; - /** - * @hidden - */ - inboundStreamOpts: InboundStreamOptions; - /** - * @hidden - */ - outboundStreamOpts: OutboundStreamOptions; - /** - * @hidden - */ - speechEvent: any; - /** - * @hidden - */ - harkSpeakingEnabled = false; - /** - * @hidden - */ - harkSpeakingEnabledOnce = false; - /** - * @hidden - */ - harkStoppedSpeakingEnabled = false; - /** - * @hidden - */ - harkStoppedSpeakingEnabledOnce = false; - /** - * @hidden - */ - harkVolumeChangeEnabled = false; - /** - * @hidden - */ - harkVolumeChangeEnabledOnce = false; - /** - * @hidden - */ - harkOptions; - /** - * @hidden - */ - localMediaStreamWhenSubscribedToRemote?: MediaStream; - /** - * @hidden - */ - ee = new EventEmitter(); - /** - * @hidden - */ - reconnectionEventEmitter: EventEmitter | undefined; - /** - * @hidden - */ - lastVideoTrackConstraints: MediaTrackConstraints | boolean | undefined; - /** - * @hidden - */ - lastVBFilter?: Filter; - - /** - * @hidden - */ - constructor(session: Session, options: InboundStreamOptions | OutboundStreamOptions | {}) { - platform = PlatformUtils.getInstance(); - this.session = session; - - if (options.hasOwnProperty('id')) { - // InboundStreamOptions: stream belongs to a Subscriber - this.inboundStreamOpts = options; - this.streamId = this.inboundStreamOpts.id; - this.creationTime = this.inboundStreamOpts.createdAt; - this.hasAudio = this.inboundStreamOpts.hasAudio; - this.hasVideo = this.inboundStreamOpts.hasVideo; - if (this.hasAudio) { - this.audioActive = this.inboundStreamOpts.audioActive; - } - if (this.hasVideo) { - this.videoActive = this.inboundStreamOpts.videoActive; - this.typeOfVideo = !this.inboundStreamOpts.typeOfVideo ? undefined : this.inboundStreamOpts.typeOfVideo; - this.frameRate = this.inboundStreamOpts.frameRate === -1 ? undefined : this.inboundStreamOpts.frameRate; - this.videoDimensions = this.inboundStreamOpts.videoDimensions; - } - if (!!this.inboundStreamOpts.filter && Object.keys(this.inboundStreamOpts.filter).length > 0) { - if ( - !!this.inboundStreamOpts.filter.lastExecMethod && - Object.keys(this.inboundStreamOpts.filter.lastExecMethod).length === 0 - ) { - delete this.inboundStreamOpts.filter.lastExecMethod; - } - this.filter = this.inboundStreamOpts.filter; - } - } else { - // OutboundStreamOptions: stream belongs to a Publisher - this.outboundStreamOpts = options; - - this.hasAudio = this.isSendAudio(); - this.hasVideo = this.isSendVideo(); - - if (this.hasAudio) { - this.audioActive = !!this.outboundStreamOpts.publisherProperties.publishAudio; - } - if (this.hasVideo) { - this.videoActive = !!this.outboundStreamOpts.publisherProperties.publishVideo; - this.frameRate = this.outboundStreamOpts.publisherProperties.frameRate; - if ( - typeof MediaStreamTrack !== 'undefined' && - this.outboundStreamOpts.publisherProperties.videoSource instanceof MediaStreamTrack - ) { - this.typeOfVideo = TypeOfVideo.CUSTOM; - } else { - this.typeOfVideo = this.isSendScreen() ? TypeOfVideo.SCREEN : TypeOfVideo.CAMERA; - } - } - if (!!this.outboundStreamOpts.publisherProperties.filter) { - this.filter = this.outboundStreamOpts.publisherProperties.filter; - } - } - - this.ee.on('mediastream-updated', () => { - this.streamManager.updateMediaStream(this.mediaStream!); - logger.debug('Video srcObject [' + this.mediaStream?.id + '] updated in stream [' + this.streamId + ']'); - }); - } - - /** - * Recreates the media connection with the server. This entails the disposal of the previous RTCPeerConnection and the re-negotiation - * of a new one, that will apply the same properties. - * - * This method can be useful in those situations were there the media connection breaks and OpenVidu is not able to recover on its own - * for any kind of unanticipated reason (see [Automatic reconnection](/en/stable/advanced-features/automatic-reconnection/)). - * - * @returns A Promise (to which you can optionally subscribe to) that is resolved if the reconnection operation was successful and rejected with an Error object if not - */ - public reconnect(): Promise { - return this.reconnectStream('API'); - } - - /** - * Applies an audio/video filter to the stream. - * - * @param type Type of filter applied. See {@link Filter.type} - * @param options Parameters used to initialize the filter. See {@link Filter.options} - * - * @returns A Promise (to which you can optionally subscribe to) that is resolved to the applied filter if success and rejected with an Error object if not - */ - applyFilter(type: string, options: Object): Promise { - return new Promise(async (resolve, reject) => { - if (!!this.filter) { - return reject( - new OpenViduError(OpenViduErrorName.GENERIC_ERROR, 'There is already a filter applied to Stream ' + this.streamId) - ); - } - - const resolveApplyFilter = (error, triggerEvent) => { - if (error) { - logger.error('Error applying filter for Stream ' + this.streamId, error); - if (error.code === 401) { - return reject( - new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to apply a filter") - ); - } else { - return reject(error); - } - } else { - logger.info('Filter successfully applied on Stream ' + this.streamId); - const oldValue: Filter = this.filter!; - this.filter = new Filter(type, options); - this.filter.stream = this; - if (triggerEvent) { - this.session.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent(this.session, this, 'filter', this.filter, oldValue, 'applyFilter') - ]); - this.streamManager.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent(this.streamManager, this, 'filter', this.filter, oldValue, 'applyFilter') - ]); - } - return resolve(this.filter); - } - }; - - if (type.startsWith('VB:')) { - // Client filters - - if (!this.hasVideo) { - return reject( - new OpenViduError( - OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, - 'The Virtual Background filter requires a video track to be applied' - ) - ); - } - if (!this.mediaStream || this.streamManager.videos.length === 0) { - return reject( - new OpenViduError( - OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, - 'The StreamManager requires some video element to be attached to it in order to apply a Virtual Background filter' - ) - ); - } - - let openviduToken: string; - if (!!this.session.token) { - openviduToken = this.session.token; - } else { - openviduToken = options['token']; - } - if (!openviduToken) { - return reject( - new OpenViduError( - OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, - 'Virtual Background requires the client to be connected to a Session or to have a "token" property available in "options" parameter with a valid OpenVidu token' - ) - ); - } - - const tokenParams = this.session.getTokenParams(openviduToken); - if (tokenParams.edition !== 'pro' && tokenParams.edition !== 'enterprise') { - return reject( - new OpenViduError( - OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, - 'OpenVidu Virtual Background API is available from OpenVidu Pro edition onwards' - ) - ); - } - - openviduToken = encodeURIComponent(btoa(openviduToken)); - - logger.info('Applying Virtual Background to stream ' + this.streamId); - - const afterScriptLoaded = async () => { - try { - const id = this.streamId + '_' + uuidv4(); - const mediaStreamClone = this.mediaStream!.clone(); - const videoClone = this.streamManager.videos[0].video.cloneNode(false) as HTMLVideoElement; - // @ts-ignore - videoClone.id = VirtualBackground.VirtualBackground.SOURCE_VIDEO_PREFIX + id; - videoClone.srcObject = mediaStreamClone; - videoClone.muted = true; - this.virtualBackgroundSourceElements = { videoClone, mediaStreamClone }; - - // @ts-ignore - VirtualBackground.VirtualBackground.hideHtmlElement(videoClone, false); - // @ts-ignore - VirtualBackground.VirtualBackground.appendHtmlElementToHiddenContainer(videoClone, id); - - await videoClone.play(); - - // @ts-ignore - const VB = new VirtualBackground.VirtualBackground({ - id, - openviduServerUrl: new URL(tokenParams.httpUri), - openviduToken, - inputVideo: videoClone, - inputResolution: '160x96', - outputFramerate: 24 - }); - - let filteredVideo: HTMLVideoElement; - switch (type) { - case 'VB:blur': { - filteredVideo = await VB.backgroundBlur(options); - break; - } - case 'VB:image': { - filteredVideo = await VB.backgroundImage(options); - break; - } - default: - throw new Error('Unknown Virtual Background filter: ' + type); - } - - this.virtualBackgroundSinkElements = { VB, video: filteredVideo }; - - videoClone.style.display = 'none'; - - if (this.streamManager.remote) { - this.streamManager.replaceTrackInMediaStream( - (this.virtualBackgroundSinkElements.video.srcObject as MediaStream).getVideoTracks()[0], - false - ); - } else { - (this.streamManager as Publisher).replaceTrackAux( - (this.virtualBackgroundSinkElements.video.srcObject as MediaStream).getVideoTracks()[0], - false - ); - } - - resolveApplyFilter(undefined, false); - } catch (error) { - if (error.name === OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR) { - resolveApplyFilter(new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, error.message), false); - } else { - resolveApplyFilter(error, false); - } - } - }; - - // @ts-ignore - if (typeof VirtualBackground === 'undefined') { - let script: HTMLScriptElement = document.createElement('script'); - script.type = 'text/javascript'; - script.src = tokenParams.httpUri + '/openvidu/virtual-background/openvidu-virtual-background.js?token=' + openviduToken; - script.onload = async () => { - try { - await afterScriptLoaded(); - resolve(new Filter(type, options)); - } catch (error) { - reject(error); - } - }; - document.body.appendChild(script); - } else { - afterScriptLoaded() - .then(() => resolve(new Filter(type, options))) - .catch((error) => reject(error)); - } - } else { - // Server filters - - if (!this.session.sessionConnected()) { - return reject(this.session.notConnectedError()); - } - - logger.info('Applying server filter to stream ' + this.streamId); - options = options != null ? options : {}; - let optionsString = options; - if (typeof optionsString !== 'string') { - optionsString = JSON.stringify(optionsString); - } - this.session.openvidu.sendRequest( - 'applyFilter', - { streamId: this.streamId, type, options: optionsString }, - (error, response) => { - resolveApplyFilter(error, true); - } - ); - } - }); - } - - /** - * Removes an audio/video filter previously applied. - * - * @returns A Promise (to which you can optionally subscribe to) that is resolved if the previously applied filter has been successfully removed and rejected with an Error object in other case - */ - async removeFilter(): Promise { - return await this.removeFilterAux(false); - } - - /** - * Returns the internal RTCPeerConnection object associated to this stream (https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection) - * - * @returns Native RTCPeerConnection Web API object - */ - getRTCPeerConnection(): RTCPeerConnection { - return this.webRtcPeer.pc; - } - - /** - * Returns the internal MediaStream object associated to this stream (https://developer.mozilla.org/en-US/docs/Web/API/MediaStream) - * - * @returns Native MediaStream Web API object - */ - getMediaStream(): MediaStream { - return this.mediaStream!; - } - - /* Hidden methods */ - - /** - * @hidden - */ - removeFilterAux(isDisposing: boolean): Promise { - return new Promise(async (resolve, reject) => { - const resolveRemoveFilter = (error, triggerEvent) => { - if (error) { - delete this.filter; - logger.error('Error removing filter for Stream ' + this.streamId, error); - if (error.code === 401) { - return reject( - new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to remove a filter") - ); - } else { - return reject(error); - } - } else { - logger.info('Filter successfully removed from Stream ' + this.streamId); - const oldValue = this.filter!; - delete this.filter; - if (triggerEvent) { - this.session.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent(this.session, this, 'filter', this.filter!, oldValue, 'applyFilter') - ]); - this.streamManager.emitEvent('streamPropertyChanged', [ - new StreamPropertyChangedEvent(this.streamManager, this, 'filter', this.filter!, oldValue, 'applyFilter') - ]); - } - return resolve(); - } - }; - - if (!!this.filter) { - // There is a filter applied - - if (this.filter?.type.startsWith('VB:')) { - // Client filters - - try { - const mediaStreamClone = this.virtualBackgroundSourceElements!.mediaStreamClone; - if (!isDisposing) { - if (this.streamManager.remote) { - this.streamManager.replaceTrackInMediaStream(mediaStreamClone.getVideoTracks()[0], false); - } else { - await (this.streamManager as Publisher).replaceTrackAux(mediaStreamClone.getVideoTracks()[0], false); - } - } else { - mediaStreamClone.getTracks().forEach((track) => track.stop()); - } - - this.virtualBackgroundSinkElements!.VB.cleanUp(); - - delete this.virtualBackgroundSinkElements; - delete this.virtualBackgroundSourceElements; - - return resolveRemoveFilter(undefined, false); - } catch (error) { - return resolveRemoveFilter(error, false); - } - } else { - // Server filters - - if (!this.session.sessionConnected()) { - return reject(this.session.notConnectedError()); - } - - logger.info('Removing filter of stream ' + this.streamId); - this.session.openvidu.sendRequest('removeFilter', { streamId: this.streamId }, (error, response) => { - return resolveRemoveFilter(error, true); - }); - } - } else { - // There is no filter applied - return reject(new OpenViduError(OpenViduErrorName.GENERIC_ERROR, 'Stream ' + this.streamId + ' has no filter applied')); - } - }); - } - - /** - * @hidden - */ - setMediaStream(mediaStream: MediaStream): void { - this.mediaStream = mediaStream; - } - - /** - * @hidden - */ - updateMediaStreamInVideos() { - this.ee.emitEvent('mediastream-updated', []); - } - - /** - * @hidden - */ - getWebRtcPeer(): WebRtcPeer { - return this.webRtcPeer; - } - - /** - * @hidden - */ - subscribeToMyRemote(value: boolean): void { - this.isSubscribeToRemote = value; - } - - /** - * @hidden - */ - setOutboundStreamOptions(outboundStreamOpts: OutboundStreamOptions): void { - this.outboundStreamOpts = outboundStreamOpts; - } - - /** - * @hidden - */ - subscribe(): Promise { - return new Promise((resolve, reject) => { - this.initWebRtcPeerReceive(false) - .then(() => resolve()) - .catch((error) => reject(error)); - }); - } - - /** - * @hidden - */ - publish(): Promise { - return new Promise((resolve, reject) => { - if (this.isLocalStreamReadyToPublish) { - this.initWebRtcPeerSend(false) - .then(() => resolve()) - .catch((error) => reject(error)); - } else { - this.ee.once('stream-ready-to-publish', () => { - this.publish() - .then(() => resolve()) - .catch((error) => reject(error)); - }); - } - }); - } - - /** - * @hidden - */ - disposeWebRtcPeer(): void { - let webrtcId; - if (!!this.webRtcPeer) { - this.webRtcPeer.dispose(); - webrtcId = this.webRtcPeer.getId(); - } - this.stopWebRtcStats(); - logger.info( - (!!this.outboundStreamOpts ? 'Outbound ' : 'Inbound ') + - 'RTCPeerConnection with id [' + - webrtcId + - "] from 'Stream' with id [" + - this.streamId + - '] is now closed' - ); - } - - /** - * @hidden - */ - async disposeMediaStream(): Promise { - if (!!this.filter && this.filter.type.startsWith('VB:')) { - try { - await this.removeFilterAux(true); - console.debug(`Success removing Virtual Background filter for stream ${this.streamId}`); - } catch (error) { - console.error(`Error removing Virtual Background filter for stream ${this.streamId}`, error); - } - } - if (this.mediaStream) { - this.mediaStream.getAudioTracks().forEach((track) => { - track.stop(); - }); - this.mediaStream.getVideoTracks().forEach((track) => { - track.stop(); - }); - delete this.mediaStream; - } - // If subscribeToRemote local MediaStream must be stopped - if (this.localMediaStreamWhenSubscribedToRemote) { - this.localMediaStreamWhenSubscribedToRemote.getAudioTracks().forEach((track) => { - track.stop(); - }); - this.localMediaStreamWhenSubscribedToRemote.getVideoTracks().forEach((track) => { - track.stop(); - }); - delete this.localMediaStreamWhenSubscribedToRemote; - } - if (!!this.speechEvent) { - if (!!this.speechEvent.stop) { - this.speechEvent.stop(); - } - delete this.speechEvent; - } - logger.info( - (!!this.outboundStreamOpts ? 'Local ' : 'Remote ') + "MediaStream from 'Stream' with id [" + this.streamId + '] is now disposed' - ); - } - - /** - * @hidden - */ - displayMyRemote(): boolean { - return this.isSubscribeToRemote; - } - - /** - * @hidden - */ - isSendAudio(): boolean { - return ( - !!this.outboundStreamOpts && - this.outboundStreamOpts.publisherProperties.audioSource !== null && - this.outboundStreamOpts.publisherProperties.audioSource !== false - ); - } - - /** - * @hidden - */ - isSendVideo(): boolean { - return ( - !!this.outboundStreamOpts && - this.outboundStreamOpts.publisherProperties.videoSource !== null && - this.outboundStreamOpts.publisherProperties.videoSource !== false - ); - } - - /** - * @hidden - */ - isSendScreen(): boolean { - let screen = false - if (typeof MediaStreamTrack !== 'undefined' && - this.outboundStreamOpts.publisherProperties.videoSource instanceof MediaStreamTrack) { - let trackSettings: any = this.outboundStreamOpts.publisherProperties.videoSource.getSettings(); - if (trackSettings.displaySurface) { - screen = ["monitor", "window", "browser"].includes(trackSettings.displaySurface); - } - } - if (!screen && platform.isElectron()) { - screen = - typeof this.outboundStreamOpts.publisherProperties.videoSource === 'string' && - this.outboundStreamOpts.publisherProperties.videoSource.startsWith('screen:'); - } - if (!screen) { - screen = this.outboundStreamOpts.publisherProperties.videoSource === 'screen'; - } - return !!this.outboundStreamOpts && screen; - } - - /** - * @hidden - */ - enableHarkSpeakingEvent(): void { - this.setHarkListenerIfNotExists(); - if (!this.harkSpeakingEnabled && !!this.speechEvent) { - this.harkSpeakingEnabled = true; - this.speechEvent.on('speaking', () => { - this.session.emitEvent('publisherStartSpeaking', [ - new PublisherSpeakingEvent(this.session, 'publisherStartSpeaking', this.connection, this.streamId) - ]); - this.streamManager.emitEvent('publisherStartSpeaking', [ - new PublisherSpeakingEvent(this.streamManager, 'publisherStartSpeaking', this.connection, this.streamId) - ]); - this.harkSpeakingEnabledOnce = false; // Disable 'once' version if 'on' version was triggered - }); - } - } - - /** - * @hidden - */ - enableOnceHarkSpeakingEvent(): void { - this.setHarkListenerIfNotExists(); - if (!this.harkSpeakingEnabledOnce && !!this.speechEvent) { - this.harkSpeakingEnabledOnce = true; - this.speechEvent.once('speaking', () => { - if (this.harkSpeakingEnabledOnce) { - // If the listener has been disabled in the meantime (for example by the 'on' version) do not trigger the event - this.session.emitEvent('publisherStartSpeaking', [ - new PublisherSpeakingEvent(this.session, 'publisherStartSpeaking', this.connection, this.streamId) - ]); - this.streamManager.emitEvent('publisherStartSpeaking', [ - new PublisherSpeakingEvent(this.streamManager, 'publisherStartSpeaking', this.connection, this.streamId) - ]); - } - this.disableHarkSpeakingEvent(true); - }); - } - } - - /** - * @hidden - */ - disableHarkSpeakingEvent(disabledByOnce: boolean): void { - if (!!this.speechEvent) { - this.harkSpeakingEnabledOnce = false; - if (disabledByOnce) { - if (this.harkSpeakingEnabled) { - // The 'on' version of this same event is enabled too. Do not remove the hark listener - return; - } - } else { - this.harkSpeakingEnabled = false; - } - // Shutting down the hark event - if ( - this.harkVolumeChangeEnabled || - this.harkVolumeChangeEnabledOnce || - this.harkStoppedSpeakingEnabled || - this.harkStoppedSpeakingEnabledOnce - ) { - // Some other hark event is enabled. Cannot stop the hark process, just remove the specific listener - this.speechEvent.off('speaking'); - } else { - // No other hark event is enabled. We can get entirely rid of it - this.speechEvent.stop(); - delete this.speechEvent; - } - } - } - - /** - * @hidden - */ - enableHarkStoppedSpeakingEvent(): void { - this.setHarkListenerIfNotExists(); - if (!this.harkStoppedSpeakingEnabled && !!this.speechEvent) { - this.harkStoppedSpeakingEnabled = true; - this.speechEvent.on('stopped_speaking', () => { - this.session.emitEvent('publisherStopSpeaking', [ - new PublisherSpeakingEvent(this.session, 'publisherStopSpeaking', this.connection, this.streamId) - ]); - this.streamManager.emitEvent('publisherStopSpeaking', [ - new PublisherSpeakingEvent(this.streamManager, 'publisherStopSpeaking', this.connection, this.streamId) - ]); - this.harkStoppedSpeakingEnabledOnce = false; // Disable 'once' version if 'on' version was triggered - }); - } - } - - /** - * @hidden - */ - enableOnceHarkStoppedSpeakingEvent(): void { - this.setHarkListenerIfNotExists(); - if (!this.harkStoppedSpeakingEnabledOnce && !!this.speechEvent) { - this.harkStoppedSpeakingEnabledOnce = true; - this.speechEvent.once('stopped_speaking', () => { - if (this.harkStoppedSpeakingEnabledOnce) { - // If the listener has been disabled in the meantime (for example by the 'on' version) do not trigger the event - this.session.emitEvent('publisherStopSpeaking', [ - new PublisherSpeakingEvent(this.session, 'publisherStopSpeaking', this.connection, this.streamId) - ]); - this.streamManager.emitEvent('publisherStopSpeaking', [ - new PublisherSpeakingEvent(this.streamManager, 'publisherStopSpeaking', this.connection, this.streamId) - ]); - } - this.disableHarkStoppedSpeakingEvent(true); - }); - } - } - - /** - * @hidden - */ - disableHarkStoppedSpeakingEvent(disabledByOnce: boolean): void { - if (!!this.speechEvent) { - this.harkStoppedSpeakingEnabledOnce = false; - if (disabledByOnce) { - if (this.harkStoppedSpeakingEnabled) { - // We are cancelling the 'once' listener for this event, but the 'on' version - // of this same event is enabled too. Do not remove the hark listener - return; - } - } else { - this.harkStoppedSpeakingEnabled = false; - } - // Shutting down the hark event - if ( - this.harkVolumeChangeEnabled || - this.harkVolumeChangeEnabledOnce || - this.harkSpeakingEnabled || - this.harkSpeakingEnabledOnce - ) { - // Some other hark event is enabled. Cannot stop the hark process, just remove the specific listener - this.speechEvent.off('stopped_speaking'); - } else { - // No other hark event is enabled. We can get entirely rid of it - this.speechEvent.stop(); - delete this.speechEvent; - } - } - } - - /** - * @hidden - */ - enableHarkVolumeChangeEvent(force: boolean): void { - if (this.setHarkListenerIfNotExists()) { - if (!this.harkVolumeChangeEnabled || force) { - this.harkVolumeChangeEnabled = true; - this.speechEvent.on('volume_change', (harkEvent) => { - const oldValue = this.speechEvent.oldVolumeValue; - const value = { newValue: harkEvent, oldValue }; - this.speechEvent.oldVolumeValue = harkEvent; - this.streamManager.emitEvent('streamAudioVolumeChange', [ - new StreamManagerEvent(this.streamManager, 'streamAudioVolumeChange', value) - ]); - }); - } - } else { - // This way whenever the MediaStream object is available, the event listener will be automatically added - this.harkVolumeChangeEnabled = true; - } - } - - /** - * @hidden - */ - enableOnceHarkVolumeChangeEvent(force: boolean): void { - if (this.setHarkListenerIfNotExists()) { - if (!this.harkVolumeChangeEnabledOnce || force) { - this.harkVolumeChangeEnabledOnce = true; - this.speechEvent.once('volume_change', (harkEvent) => { - const oldValue = this.speechEvent.oldVolumeValue; - const value = { newValue: harkEvent, oldValue }; - this.speechEvent.oldVolumeValue = harkEvent; - this.disableHarkVolumeChangeEvent(true); - this.streamManager.emitEvent('streamAudioVolumeChange', [ - new StreamManagerEvent(this.streamManager, 'streamAudioVolumeChange', value) - ]); - }); - } - } else { - // This way whenever the MediaStream object is available, the event listener will be automatically added - this.harkVolumeChangeEnabledOnce = true; - } - } - - /** - * @hidden - */ - disableHarkVolumeChangeEvent(disabledByOnce: boolean): void { - if (!!this.speechEvent) { - this.harkVolumeChangeEnabledOnce = false; - if (disabledByOnce) { - if (this.harkVolumeChangeEnabled) { - // We are cancelling the 'once' listener for this event, but the 'on' version - // of this same event is enabled too. Do not remove the hark listener - return; - } - } else { - this.harkVolumeChangeEnabled = false; - } - // Shutting down the hark event - if ( - this.harkSpeakingEnabled || - this.harkSpeakingEnabledOnce || - this.harkStoppedSpeakingEnabled || - this.harkStoppedSpeakingEnabledOnce - ) { - // Some other hark event is enabled. Cannot stop the hark process, just remove the specific listener - this.speechEvent.off('volume_change'); - } else { - // No other hark event is enabled. We can get entirely rid of it - this.speechEvent.stop(); - delete this.speechEvent; - } - } - } - - /** - * @hidden - */ - isLocal(): boolean { - // inbound options undefined and outbound options defined - return !this.inboundStreamOpts && !!this.outboundStreamOpts; - } - - /** - * @hidden - */ - getSelectedIceCandidate(): Promise { - return new Promise((resolve, reject) => { - this.webRtcStats - .getSelectedIceCandidateInfo() - .then((report) => resolve(report)) - .catch((error) => reject(error)); - }); - } - - /** - * @hidden - */ - getRemoteIceCandidateList(): RTCIceCandidate[] { - return this.webRtcPeer.remoteCandidatesQueue; - } - - /** - * @hidden - */ - getLocalIceCandidateList(): RTCIceCandidate[] { - return this.webRtcPeer.localCandidatesQueue; - } - - /** - * @hidden - */ - streamIceConnectionStateBroken() { - if (!this.getWebRtcPeer() || !this.getRTCPeerConnection()) { - return false; - } - if (!!this.session.openvidu.advancedConfiguration.forceMediaReconnectionAfterNetworkDrop) { - logger.warn( - `OpenVidu Browser advanced configuration option "forceMediaReconnectionAfterNetworkDrop" is enabled. Stream ${this.streamId - } (${this.isLocal() ? 'Publisher' : 'Subscriber'}) will force a reconnection` - ); - return true; - } else { - const iceConnectionState: RTCIceConnectionState = this.getRTCPeerConnection().iceConnectionState; - return iceConnectionState !== 'connected' && iceConnectionState !== 'completed'; - } - } - - /* Private methods */ - - private setHarkListenerIfNotExists(): boolean { - if (!!this.mediaStream) { - if (!this.speechEvent) { - const harkOptions = !!this.harkOptions - ? this.harkOptions - : this.session.openvidu.advancedConfiguration.publisherSpeakingEventsOptions || {}; - harkOptions.interval = typeof harkOptions.interval === 'number' ? harkOptions.interval : 100; - harkOptions.threshold = typeof harkOptions.threshold === 'number' ? harkOptions.threshold : -50; - this.speechEvent = hark(this.mediaStream, harkOptions); - } - return true; - } - return false; - } - - /** - * @hidden - */ - setupReconnectionEventEmitter(resolve: (value: void | PromiseLike) => void, reject: (reason?: any) => void): boolean { - if (this.reconnectionEventEmitter == undefined) { - // There is no ongoing reconnection - this.reconnectionEventEmitter = new EventEmitter(); - return false; - } else { - // Ongoing reconnection - console.warn( - `Trying to reconnect stream ${this.streamId} (${this.isLocal() ? 'Publisher' : 'Subscriber' - }) but an ongoing reconnection process is active. Waiting for response...` - ); - this.reconnectionEventEmitter.once('success', () => resolve()); - this.reconnectionEventEmitter.once('error', (error) => reject(error)); - return true; - } - } - - /** - * @hidden - */ - initWebRtcPeerSend(reconnect: boolean): Promise { - return new Promise((resolve, reject) => { - if (reconnect) { - if (this.setupReconnectionEventEmitter(resolve, reject)) { - // Ongoing reconnection - return; - } - } else { - // MediaStream will already have hark events for reconnected streams - this.initHarkEvents(); // Init hark events for the local stream - } - - const finalResolve = () => { - if (reconnect) { - this.reconnectionEventEmitter?.emitEvent('success'); - delete this.reconnectionEventEmitter; - } - return resolve(); - }; - - const finalReject = (error) => { - if (reconnect) { - this.reconnectionEventEmitter?.emitEvent('error', [error]); - delete this.reconnectionEventEmitter; - } - return reject(error); - }; - - const successOfferCallback = (sdpOfferParam) => { - logger.debug('Sending SDP offer to publish as ' + this.streamId, sdpOfferParam); - - const method = reconnect ? 'reconnectStream' : 'publishVideo'; - let params; - if (reconnect) { - params = { - stream: this.streamId, - sdpString: sdpOfferParam - }; - } else { - let typeOfVideo; - if (this.isSendVideo()) { - typeOfVideo = - typeof MediaStreamTrack !== 'undefined' && - this.outboundStreamOpts.publisherProperties.videoSource instanceof MediaStreamTrack - ? TypeOfVideo.CUSTOM - : this.isSendScreen() - ? TypeOfVideo.SCREEN - : TypeOfVideo.CAMERA; - } - params = { - doLoopback: this.displayMyRemote() || false, - hasAudio: this.isSendAudio(), - hasVideo: this.isSendVideo(), - audioActive: this.audioActive, - videoActive: this.videoActive, - typeOfVideo, - frameRate: !!this.frameRate ? this.frameRate : -1, - videoDimensions: JSON.stringify(this.videoDimensions), - filter: this.outboundStreamOpts.publisherProperties.filter, - sdpOffer: sdpOfferParam - }; - } - - this.session.openvidu.sendRequest(method, params, (error, response) => { - if (error) { - if (error.code === 401) { - finalReject( - new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to publish") - ); - } else { - finalReject('Error on publishVideo: ' + JSON.stringify(error)); - } - } else { - this.webRtcPeer - .processRemoteAnswer(response.sdpAnswer) - .then(() => { - this.streamId = response.id; - this.creationTime = response.createdAt; - this.isLocalStreamPublished = true; - this.publishedOnce = true; - if (this.displayMyRemote()) { - this.localMediaStreamWhenSubscribedToRemote = this.mediaStream; - this.remotePeerSuccessfullyEstablished(reconnect); - } - if (reconnect) { - this.ee.emitEvent('stream-reconnected-by-publisher', []); - } else { - this.ee.emitEvent('stream-created-by-publisher', []); - } - this.initWebRtcStats(); - logger.info( - "'Publisher' (" + - this.streamId + - ') successfully ' + - (reconnect ? 'reconnected' : 'published') + - ' to session' - ); - - finalResolve(); - }) - .catch((error) => { - finalReject(error); - }); - } - }); - }; - - const config: WebRtcPeerConfiguration = { - mediaConstraints: { - audio: this.hasAudio, - video: this.hasVideo - }, - simulcast: this.outboundStreamOpts.publisherProperties.videoSimulcast ?? this.session.openvidu.videoSimulcast, - onIceCandidate: this.connection.sendIceCandidate.bind(this.connection), - onIceConnectionStateException: this.onIceConnectionStateExceptionHandler.bind(this), - iceServers: this.getIceServersConf(), - rtcConfiguration: this.session.openvidu.advancedConfiguration.rtcConfiguration, - mediaStream: this.mediaStream, - mediaServer: this.session.openvidu.mediaServer, - typeOfVideo: this.typeOfVideo ? TypeOfVideo[this.typeOfVideo] : undefined - }; - - if (this.session.openvidu.mediaServer !== 'mediasoup') { - // Simulcast is only supported by mediasoup - config.simulcast = false; - } - - if (reconnect) { - this.disposeWebRtcPeer(); - } - if (this.displayMyRemote()) { - this.webRtcPeer = new WebRtcPeerSendrecv(config); - } else { - this.webRtcPeer = new WebRtcPeerSendonly(config); - } - this.webRtcPeer.addIceConnectionStateChangeListener('publisher of ' + this.connection.connectionId); - this.webRtcPeer - .createOffer() - .then((sdpOffer) => { - this.webRtcPeer - .processLocalOffer(sdpOffer) - .then(() => { - successOfferCallback(sdpOffer.sdp); - }) - .catch((error) => { - finalReject(new Error('(publish) SDP process local offer error: ' + JSON.stringify(error))); - }); - }) - .catch((error) => { - finalReject(new Error('(publish) SDP create offer error: ' + JSON.stringify(error))); - }); - }); - } - - /** - * @hidden - */ - finalResolveForSubscription(reconnect: boolean, resolve: (value: void | PromiseLike) => void) { - logger.info("'Subscriber' (" + this.streamId + ') successfully ' + (reconnect ? 'reconnected' : 'subscribed')); - this.remotePeerSuccessfullyEstablished(reconnect); - this.initWebRtcStats(); - if (reconnect) { - this.reconnectionEventEmitter?.emitEvent('success'); - delete this.reconnectionEventEmitter; - } - return resolve(); - } - - /** - * @hidden - */ - finalRejectForSubscription(reconnect: boolean, error: any, reject: (reason?: any) => void) { - logger.error( - "Error for 'Subscriber' (" + - this.streamId + - ') while trying to ' + - (reconnect ? 'reconnect' : 'subscribe') + - ': ' + - error.toString() - ); - if (reconnect) { - this.reconnectionEventEmitter?.emitEvent('error', [error]); - delete this.reconnectionEventEmitter; - } - return reject(error); - } - - /** - * @hidden - */ - initWebRtcPeerReceive(reconnect: boolean): Promise { - return new Promise((resolve, reject) => { - if (reconnect) { - if (this.setupReconnectionEventEmitter(resolve, reject)) { - // Ongoing reconnection - return; - } - } - - if (this.session.openvidu.mediaServer === 'mediasoup') { - // Server initiates negotiation - - this.initWebRtcPeerReceiveFromServer(reconnect) - .then(() => this.finalResolveForSubscription(reconnect, resolve)) - .catch((error) => this.finalRejectForSubscription(reconnect, error, reject)); - } else { - // Client initiates negotiation - - this.initWebRtcPeerReceiveFromClient(reconnect) - .then(() => this.finalResolveForSubscription(reconnect, resolve)) - .catch((error) => this.finalRejectForSubscription(reconnect, error, reject)); - } - }); - } - - /** - * @hidden - */ - initWebRtcPeerReceiveFromClient(reconnect: boolean): Promise { - return new Promise((resolve, reject) => { - this.completeWebRtcPeerReceive(reconnect, false) - .then((response) => { - this.webRtcPeer - .processRemoteAnswer(response.sdpAnswer) - .then(() => resolve()) - .catch((error) => reject(error)); - }) - .catch((error) => reject(error)); - }); - } - - /** - * @hidden - */ - initWebRtcPeerReceiveFromServer(reconnect: boolean): Promise { - return new Promise((resolve, reject) => { - // Server initiates negotiation - this.session.openvidu.sendRequest('prepareReceiveVideoFrom', { sender: this.streamId, reconnect }, (error, response) => { - if (error) { - return reject(new Error('Error on prepareReceiveVideoFrom: ' + JSON.stringify(error))); - } else { - this.completeWebRtcPeerReceive(reconnect, false, response.sdpOffer) - .then(() => resolve()) - .catch((error) => reject(error)); - } - }); - }); - } - - /** - * @hidden - */ - completeWebRtcPeerReceive(reconnect: boolean, forciblyReconnect: boolean, sdpOfferByServer?: string): Promise { - return new Promise((resolve, reject) => { - logger.debug("'Session.subscribe(Stream)' called"); - - const sendSdpToServer = (sdpString: string) => { - logger.debug(`Sending local SDP ${!!sdpOfferByServer ? 'answer' : 'offer'} to subscribe to ${this.streamId}`, sdpString); - - const method = reconnect ? 'reconnectStream' : 'receiveVideoFrom'; - const params = {}; - params[reconnect ? 'stream' : 'sender'] = this.streamId; - if (!!sdpOfferByServer) { - params[reconnect ? 'sdpString' : 'sdpAnswer'] = sdpString; - } else { - params['sdpOffer'] = sdpString; - } - if (reconnect) { - params['forciblyReconnect'] = forciblyReconnect; - } - - this.session.openvidu.sendRequest(method, params, (error, response) => { - if (error) { - return reject(new Error('Error on ' + method + ' : ' + JSON.stringify(error))); - } else { - return resolve(response); - } - }); - }; - - const config: WebRtcPeerConfiguration = { - mediaConstraints: { - audio: this.hasAudio, - video: this.hasVideo - }, - simulcast: false, - onIceCandidate: this.connection.sendIceCandidate.bind(this.connection), - onIceConnectionStateException: this.onIceConnectionStateExceptionHandler.bind(this), - iceServers: this.getIceServersConf(), - rtcConfiguration: this.session.openvidu.advancedConfiguration.rtcConfiguration, - mediaServer: this.session.openvidu.mediaServer, - typeOfVideo: this.typeOfVideo ? TypeOfVideo[this.typeOfVideo] : undefined - }; - - if (reconnect) { - this.disposeWebRtcPeer(); - } - - this.webRtcPeer = new WebRtcPeerRecvonly(config); - this.webRtcPeer.addIceConnectionStateChangeListener(this.streamId); - - if (!!sdpOfferByServer) { - this.webRtcPeer - .processRemoteOffer(sdpOfferByServer) - .then(() => { - this.webRtcPeer - .createAnswer() - .then((sdpAnswer) => { - this.webRtcPeer - .processLocalAnswer(sdpAnswer) - .then(() => { - sendSdpToServer(sdpAnswer.sdp!); - }) - .catch((error) => { - return reject(new Error('(subscribe) SDP process local answer error: ' + JSON.stringify(error))); - }); - }) - .catch((error) => { - return reject(new Error('(subscribe) SDP create answer error: ' + JSON.stringify(error))); - }); - }) - .catch((error) => { - return reject(new Error('(subscribe) SDP process remote offer error: ' + JSON.stringify(error))); - }); - } else { - this.webRtcPeer - .createOffer() - .then((sdpOffer) => { - this.webRtcPeer - .processLocalOffer(sdpOffer) - .then(() => { - sendSdpToServer(sdpOffer.sdp!); - }) - .catch((error) => { - return reject(new Error('(subscribe) SDP process local offer error: ' + JSON.stringify(error))); - }); - }) - .catch((error) => { - return reject(new Error('(subscribe) SDP create offer error: ' + JSON.stringify(error))); - }); - } - }); - } - - /** - * @hidden - */ - remotePeerSuccessfullyEstablished(reconnect: boolean): void { - if (reconnect && this.mediaStream != null) { - // Now we can destroy the existing MediaStream - this.disposeMediaStream(); - } - - this.mediaStream = new MediaStream(); - let receiver: RTCRtpReceiver; - for (receiver of this.webRtcPeer.pc.getReceivers()) { - if (!!receiver.track) { - this.mediaStream.addTrack(receiver.track); - } - } - logger.debug('Peer remote stream', this.mediaStream); - - if (!!this.mediaStream) { - if (this.streamManager instanceof Subscriber) { - // Apply SubscriberProperties.subscribeToAudio and SubscriberProperties.subscribeToVideo - if (!!this.mediaStream.getAudioTracks()[0]) { - const enabled = reconnect ? this.audioActive : !!(this.streamManager as Subscriber).properties.subscribeToAudio; - this.mediaStream.getAudioTracks()[0].enabled = enabled; - } - if (!!this.mediaStream.getVideoTracks()[0]) { - const enabled = reconnect ? this.videoActive : !!this.videoActive && !!(this.streamManager as Subscriber).properties.subscribeToVideo; - this.mediaStream.getVideoTracks()[0].enabled = enabled; - } - } - - this.updateMediaStreamInVideos(); - this.initHarkEvents(); // Init hark events for the remote stream - } - } - - /** - * @hidden - */ - initHarkEvents(): void { - if (!!this.mediaStream!.getAudioTracks()[0]) { - // Hark events can only be set if audio track is available - if (this.session.anySpeechEventListenerEnabled('publisherStartSpeaking', true, this.streamManager)) { - this.enableOnceHarkSpeakingEvent(); - } - if (this.session.anySpeechEventListenerEnabled('publisherStartSpeaking', false, this.streamManager)) { - this.enableHarkSpeakingEvent(); - } - if (this.session.anySpeechEventListenerEnabled('publisherStopSpeaking', true, this.streamManager)) { - this.enableOnceHarkStoppedSpeakingEvent(); - } - if (this.session.anySpeechEventListenerEnabled('publisherStopSpeaking', false, this.streamManager)) { - this.enableHarkStoppedSpeakingEvent(); - } - if (this.harkVolumeChangeEnabledOnce) { - this.enableOnceHarkVolumeChangeEvent(true); - } - if (this.harkVolumeChangeEnabled) { - this.enableHarkVolumeChangeEvent(true); - } - } - } - - private onIceConnectionStateExceptionHandler(exceptionName: ExceptionEventName, message: string, data?: any): void { - switch (exceptionName) { - case ExceptionEventName.ICE_CONNECTION_FAILED: - this.onIceConnectionFailed(); - break; - case ExceptionEventName.ICE_CONNECTION_DISCONNECTED: - this.onIceConnectionDisconnected(); - break; - } - this.session.emitEvent('exception', [new ExceptionEvent(this.session, exceptionName, this, message, data)]); - } - - private onIceConnectionFailed() { - // Immediately reconnect, as this is a terminal error - logger.log( - `[ICE_CONNECTION_FAILED] Handling ICE_CONNECTION_FAILED event. Reconnecting stream ${this.streamId} (${this.isLocal() ? 'Publisher' : 'Subscriber' - })` - ); - this.reconnectStreamAndLogResultingIceConnectionState(ExceptionEventName.ICE_CONNECTION_FAILED); - } - - private onIceConnectionDisconnected() { - // Wait to see if the ICE connection is able to reconnect - logger.log( - `[ICE_CONNECTION_DISCONNECTED] Handling ICE_CONNECTION_DISCONNECTED event. Waiting for ICE to be restored and reconnect stream ${this.streamId - } (${this.isLocal() ? 'Publisher' : 'Subscriber'}) if not possible` - ); - const timeout = this.session.openvidu.advancedConfiguration.iceConnectionDisconnectedExceptionTimeout || 4000; - this.awaitWebRtcPeerConnectionState(timeout).then((state) => { - switch (state) { - case 'failed': - // Do nothing, as an ICE_CONNECTION_FAILED event will have already raised - logger.warn( - `[ICE_CONNECTION_DISCONNECTED] ICE connection of stream ${this.streamId} (${this.isLocal() ? 'Publisher' : 'Subscriber' - }) is now failed after ICE_CONNECTION_DISCONNECTED` - ); - break; - case 'connected': - case 'completed': - logger.log( - `[ICE_CONNECTION_DISCONNECTED] ICE connection of stream ${this.streamId} (${this.isLocal() ? 'Publisher' : 'Subscriber' - }) automatically restored after ICE_CONNECTION_DISCONNECTED. Current ICE connection state: ${state}` - ); - break; - case 'closed': - case 'checking': - case 'new': - case 'disconnected': - // Rest of states - logger.warn( - `[ICE_CONNECTION_DISCONNECTED] ICE connection of stream ${this.streamId} (${this.isLocal() ? 'Publisher' : 'Subscriber' - }) couldn't be restored after ICE_CONNECTION_DISCONNECTED event. Current ICE connection state after ${timeout} ms: ${state}` - ); - this.reconnectStreamAndLogResultingIceConnectionState(ExceptionEventName.ICE_CONNECTION_DISCONNECTED); - break; - } - }); - } - - private async reconnectStreamAndLogResultingIceConnectionState(event: string) { - try { - const finalIceStateAfterReconnection = await this.reconnectStreamAndReturnIceConnectionState(event); - switch (finalIceStateAfterReconnection) { - case 'connected': - case 'completed': - logger.log( - `[${event}] Stream ${this.streamId} (${this.isLocal() ? 'Publisher' : 'Subscriber' - }) successfully reconnected after ${event}. Current ICE connection state: ${finalIceStateAfterReconnection}` - ); - break; - default: - logger.error( - `[${event}] Stream ${this.streamId} (${this.isLocal() ? 'Publisher' : 'Subscriber' - }) failed to reconnect after ${event}. Current ICE connection state: ${finalIceStateAfterReconnection}` - ); - break; - } - } catch (error) { - logger.error( - `[${event}] Error reconnecting stream ${this.streamId} (${this.isLocal() ? 'Publisher' : 'Subscriber' - }) after ${event}: ${error}` - ); - } - } - - private async reconnectStreamAndReturnIceConnectionState(event: string): Promise { - logger.log(`[${event}] Reconnecting stream ${this.streamId} (${this.isLocal() ? 'Publisher' : 'Subscriber'}) after event ${event}`); - try { - await this.reconnectStream(event); - const timeout = this.session.openvidu.advancedConfiguration.iceConnectionDisconnectedExceptionTimeout || 4000; - return this.awaitWebRtcPeerConnectionState(timeout); - } catch (error) { - logger.warn( - `[${event}] Error reconnecting stream ${this.streamId} (${this.isLocal() ? 'Publisher' : 'Subscriber'}). Reason: ${error}` - ); - return this.awaitWebRtcPeerConnectionState(1); - } - } - - private async awaitWebRtcPeerConnectionState(timeout: number): Promise { - let state = this.getRTCPeerConnection().iceConnectionState; - const interval = 150; - const intervals = Math.ceil(timeout / interval); - for (let i = 0; i < intervals; i++) { - state = this.getRTCPeerConnection().iceConnectionState; - if (state === 'connected' || state === 'completed') { - break; - } - // Sleep - await new Promise((resolve) => setTimeout(resolve, interval)); - } - return state; - } - - private async reconnectStream(event: string) { - const isWsConnected = await this.isWebsocketConnected(event, 3000); - if (isWsConnected) { - // There is connection to openvidu-server. The RTCPeerConnection is the only one broken - logger.log( - `[${event}] Trying to reconnect stream ${this.streamId} (${this.isLocal() ? 'Publisher' : 'Subscriber' - }) and the websocket is opened` - ); - if (this.isLocal()) { - return this.initWebRtcPeerSend(true); - } else { - return this.initWebRtcPeerReceive(true); - } - } else { - // There is no connection to openvidu-server. Nothing can be done. The automatic reconnection - // feature should handle a possible reconnection of RTCPeerConnection in case network comes back - const errorMsg = `[${event}] Trying to reconnect stream ${this.streamId} (${this.isLocal() ? 'Publisher' : 'Subscriber' - }) but the websocket wasn't opened`; - logger.error(errorMsg); - throw Error(errorMsg); - } - } - - private isWebsocketConnected(event: string, msResponseTimeout: number): Promise { - return new Promise((resolve, reject) => { - const wsReadyState = this.session.openvidu.getWsReadyState(); - if (wsReadyState === 1) { - const responseTimeout = setTimeout(() => { - console.warn(`[${event}] Websocket timeout of ${msResponseTimeout}ms`); - return resolve(false); - }, msResponseTimeout); - this.session.openvidu.sendRequest('echo', {}, (error, response) => { - clearTimeout(responseTimeout); - if (!!error) { - console.warn(`[${event}] Websocket 'echo' returned error: ${error}`); - return resolve(false); - } else { - return resolve(true); - } - }); - } else { - console.warn(`[${event}] Websocket readyState is ${wsReadyState}`); - return resolve(false); - } - }); - } - - /** - * @hidden - */ - initWebRtcStats(): void { - this.webRtcStats = new WebRtcStats(this); - this.webRtcStats.initWebRtcStats(); - - //TODO: send common webrtc stats from client to openvidu-server - /*if (this.session.openvidu.webrtcStatsInterval > 0) { - setInterval(() => { - this.gatherStatsForPeer().then(jsonStats => { - const body = { - sessionId: this.session.sessionId, - participantPrivateId: this.connection.rpcSessionId, - stats: jsonStats - } - var xhr = new XMLHttpRequest(); - xhr.open('POST', this.session.openvidu.httpUri + '/elasticsearch/webrtc-stats', true); - xhr.setRequestHeader('Content-Type', 'application/json'); - xhr.send(JSON.stringify(body)); - }) - }, this.session.openvidu.webrtcStatsInterval * 1000); - }*/ - } - - private stopWebRtcStats(): void { - if (!!this.webRtcStats && this.webRtcStats.isEnabled()) { - this.webRtcStats.stopWebRtcStats(); - } - } - - private getIceServersConf(): RTCIceServer[] | undefined { - let returnValue; - if (!!this.session.openvidu.advancedConfiguration.iceServers) { - // Priority 1: OpenViduAdvancedConfiguration.iceServers - returnValue = - this.session.openvidu.advancedConfiguration.iceServers === 'freeice' - ? undefined - : this.session.openvidu.advancedConfiguration.iceServers; - } else if (!!this.session.openvidu.advancedConfiguration.rtcConfiguration?.iceServers) { - // Priority 2: OpenViduAdvancedConfiguration.rtcConfiguration.iceServers - returnValue = this.session.openvidu.advancedConfiguration.rtcConfiguration.iceServers; - } else if (this.session.openvidu.iceServers) { - // Priority 3: default ICE servers sent by openvidu-server - returnValue = this.session.openvidu.iceServers; - } else { - // Priority 4: freeice STUN servers - returnValue = undefined; - } - return returnValue; - } - - private gatherStatsForPeer(): Promise { - return new Promise((resolve, reject) => { - if (this.isLocal()) { - // Publisher stream stats - - this.getRTCPeerConnection() - .getSenders() - .forEach((sender) => - sender.getStats().then((response) => { - response.forEach((report) => { - if (this.isReportWanted(report)) { - const finalReport = {}; - - finalReport['type'] = report.type; - finalReport['timestamp'] = report.timestamp; - finalReport['id'] = report.id; - - // Common to Chrome, Firefox and Safari - if (report.type === 'outbound-rtp') { - finalReport['ssrc'] = report.ssrc; - finalReport['firCount'] = report.firCount; - finalReport['pliCount'] = report.pliCount; - finalReport['nackCount'] = report.nackCount; - finalReport['qpSum'] = report.qpSum; - - // Set media type - if (!!report.kind) { - finalReport['mediaType'] = report.kind; - } else if (!!report.mediaType) { - finalReport['mediaType'] = report.mediaType; - } else { - // Safari does not have 'mediaType' defined for inbound-rtp. Must be inferred from 'id' field - finalReport['mediaType'] = report.id.indexOf('VideoStream') !== -1 ? 'video' : 'audio'; - } - - if (finalReport['mediaType'] === 'video') { - finalReport['framesEncoded'] = report.framesEncoded; - } - - finalReport['packetsSent'] = report.packetsSent; - finalReport['bytesSent'] = report.bytesSent; - } - - // Only for Chrome and Safari - if (report.type === 'candidate-pair' && report.totalRoundTripTime !== undefined) { - // This is the final selected candidate pair - finalReport['availableOutgoingBitrate'] = report.availableOutgoingBitrate; - finalReport['rtt'] = report.currentRoundTripTime; - finalReport['averageRtt'] = report.totalRoundTripTime / report.responsesReceived; - } - - // Only for Firefox >= 66.0 - if (report.type === 'remote-inbound-rtp' || report.type === 'remote-outbound-rtp') { - } - - logger.log(finalReport); - } - }); - }) - ); - } else { - // Subscriber stream stats - - this.getRTCPeerConnection() - .getReceivers() - .forEach((receiver) => - receiver.getStats().then((response) => { - response.forEach((report) => { - if (this.isReportWanted(report)) { - const finalReport = {}; - - finalReport['type'] = report.type; - finalReport['timestamp'] = report.timestamp; - finalReport['id'] = report.id; - - // Common to Chrome, Firefox and Safari - if (report.type === 'inbound-rtp') { - finalReport['ssrc'] = report.ssrc; - finalReport['firCount'] = report.firCount; - finalReport['pliCount'] = report.pliCount; - finalReport['nackCount'] = report.nackCount; - finalReport['qpSum'] = report.qpSum; - - // Set media type - if (!!report.kind) { - finalReport['mediaType'] = report.kind; - } else if (!!report.mediaType) { - finalReport['mediaType'] = report.mediaType; - } else { - // Safari does not have 'mediaType' defined for inbound-rtp. Must be inferred from 'id' field - finalReport['mediaType'] = report.id.indexOf('VideoStream') !== -1 ? 'video' : 'audio'; - } - - if (finalReport['mediaType'] === 'video') { - finalReport['framesDecoded'] = report.framesDecoded; - } - - finalReport['packetsReceived'] = report.packetsReceived; - finalReport['packetsLost'] = report.packetsLost; - finalReport['jitter'] = report.jitter; - finalReport['bytesReceived'] = report.bytesReceived; - } - - // Only for Chrome and Safari - if (report.type === 'candidate-pair' && report.totalRoundTripTime !== undefined) { - // This is the final selected candidate pair - finalReport['availableIncomingBitrate'] = report.availableIncomingBitrate; - finalReport['rtt'] = report.currentRoundTripTime; - finalReport['averageRtt'] = report.totalRoundTripTime / report.responsesReceived; - } - - // Only for Firefox >= 66.0 - if (report.type === 'remote-inbound-rtp' || report.type === 'remote-outbound-rtp') { - } - logger.log(finalReport); - } - }); - }) - ); - } - }); - } - - private isReportWanted(report: any): boolean { - return ( - (report.type === 'inbound-rtp' && !this.isLocal()) || - (report.type === 'outbound-rtp' && this.isLocal()) || - (report.type === 'candidate-pair' && report.nominated && report.bytesSent > 0) - ); - } -} diff --git a/openvidu-browser/src/OpenVidu/StreamManager.ts b/openvidu-browser/src/OpenVidu/StreamManager.ts deleted file mode 100644 index 23251a4e..00000000 --- a/openvidu-browser/src/OpenVidu/StreamManager.ts +++ /dev/null @@ -1,621 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Stream } from './Stream'; -import { Subscriber } from './Subscriber'; -import { EventDispatcher } from './EventDispatcher'; -import { StreamManagerVideo } from '../OpenViduInternal/Interfaces/Public/StreamManagerVideo'; -import { StreamManagerEventMap } from '../OpenViduInternal/Events/EventMap/StreamManagerEventMap'; -import { StreamManagerEvent } from '../OpenViduInternal/Events/StreamManagerEvent'; -import { VideoElementEvent } from '../OpenViduInternal/Events/VideoElementEvent'; -import { ExceptionEvent, ExceptionEventName } from '../OpenViduInternal/Events/ExceptionEvent'; -import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode'; -import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; -import { PlatformUtils } from '../OpenViduInternal/Utils/Platform'; - -/** - * @hidden - */ -const logger: OpenViduLogger = OpenViduLogger.getInstance(); - -/** - * @hidden - */ -let platform: PlatformUtils; - -/** - * Interface in charge of displaying the media streams in the HTML DOM. This wraps any {@link Publisher} and {@link Subscriber} object. - * You can insert as many video players fo the same Stream as you want by calling {@link StreamManager.addVideoElement} or - * {@link StreamManager.createVideoElement}. - * The use of StreamManager wrapper is particularly useful when you don't need to differentiate between Publisher or Subscriber streams or just - * want to directly manage your own video elements (even more than one video element per Stream). This scenario is pretty common in - * declarative, MVC frontend frameworks such as Angular, React or Vue.js - * - * See available event listeners at {@link StreamManagerEventMap}. - */ -export abstract class StreamManager extends EventDispatcher { - /** - * The Stream represented in the DOM by the Publisher/Subscriber - */ - stream: Stream; - - /** - * All the videos displaying the Stream of this Publisher/Subscriber - */ - videos: StreamManagerVideo[] = []; - - /** - * Whether the Stream represented in the DOM is local or remote - * - `false` for {@link Publisher} - * - `true` for {@link Subscriber} - */ - remote: boolean; - - /** - * The DOM HTMLElement assigned as target element when creating the video for the Publisher/Subscriber. This property is only defined if: - * - {@link Publisher} has been initialized by calling method {@link OpenVidu.initPublisher} with a valid `targetElement` parameter - * - {@link Subscriber} has been initialized by calling method {@link Session.subscribe} with a valid `targetElement` parameter - */ - targetElement: HTMLElement; - - /** - * `id` attribute of the DOM video element displaying the Publisher/Subscriber's stream. This property is only defined if: - * - {@link Publisher} has been initialized by calling method {@link OpenVidu.initPublisher} with a valid `targetElement` parameter - * - {@link Subscriber} has been initialized by calling method {@link Session.subscribe} with a valid `targetElement` parameter - */ - id: string; - - /** - * @hidden - */ - protected firstVideoElement?: StreamManagerVideo; - /** - * @hidden - */ - protected element: HTMLElement; - /** - * @hidden - */ - protected canPlayListener: EventListener; - /** - * @hidden - */ - private streamPlayingEventExceptionTimeout?: NodeJS.Timeout; - /** - * @hidden - */ - private lazyLaunchVideoElementCreatedEvent = false; - - /** - * @hidden - */ - constructor(stream: Stream, targetElement?: HTMLElement | string) { - super(); - platform = PlatformUtils.getInstance(); - this.stream = stream; - this.stream.streamManager = this; - this.remote = !this.stream.isLocal(); - - if (!!targetElement) { - let targEl; - if (typeof targetElement === 'string') { - targEl = document.getElementById(targetElement); - } else if (targetElement instanceof HTMLElement) { - targEl = targetElement; - } - - if (!!targEl) { - this.firstVideoElement = { - targetElement: targEl, - video: document.createElement('video'), - id: '', - canplayListenerAdded: false - }; - if ( - platform.isSafariBrowser() || - (platform.isIPhoneOrIPad() && - (platform.isChromeMobileBrowser() || - platform.isEdgeMobileBrowser() || - platform.isOperaMobileBrowser() || - platform.isFirefoxMobileBrowser())) - ) { - this.firstVideoElement.video.playsInline = true; - } - this.targetElement = targEl; - this.element = targEl; - } - } - - this.canPlayListener = () => { - this.deactivateStreamPlayingEventExceptionTimeout(); - this.ee.emitEvent('streamPlaying', [new StreamManagerEvent(this, 'streamPlaying', undefined)]); - }; - } - - /** - * See {@link EventDispatcher.on} - */ - on(type: K, handler: (event: StreamManagerEventMap[K]) => void): this { - super.onAux(type, "Event '" + type + "' triggered by '" + (this.remote ? 'Subscriber' : 'Publisher') + "'", handler); - - if (type === 'videoElementCreated') { - if (!!this.stream && this.lazyLaunchVideoElementCreatedEvent) { - this.ee.emitEvent('videoElementCreated', [new VideoElementEvent(this.videos[0].video, this, 'videoElementCreated')]); - this.lazyLaunchVideoElementCreatedEvent = false; - } - } - if (type === 'streamPlaying') { - if ( - this.videos[0] && - this.videos[0].video && - this.videos[0].video.currentTime > 0 && - this.videos[0].video.paused === false && - this.videos[0].video.ended === false && - this.videos[0].video.readyState === 4 - ) { - this.ee.emitEvent('streamPlaying', [new StreamManagerEvent(this, 'streamPlaying', undefined)]); - } - } - if (this.stream.hasAudio) { - if (type === 'publisherStartSpeaking') { - this.stream.enableHarkSpeakingEvent(); - } - if (type === 'publisherStopSpeaking') { - this.stream.enableHarkStoppedSpeakingEvent(); - } - if (type === 'streamAudioVolumeChange') { - this.stream.enableHarkVolumeChangeEvent(false); - } - } - return this; - } - - /** - * See {@link EventDispatcher.once} - */ - once(type: K, handler: (event: StreamManagerEventMap[K]) => void): this { - super.onceAux(type, "Event '" + type + "' triggered once by '" + (this.remote ? 'Subscriber' : 'Publisher') + "'", handler); - - if (type === 'videoElementCreated') { - if (!!this.stream && this.lazyLaunchVideoElementCreatedEvent) { - this.ee.emitEvent('videoElementCreated', [new VideoElementEvent(this.videos[0].video, this, 'videoElementCreated')]); - } - } - if (type === 'streamPlaying') { - if ( - this.videos[0] && - this.videos[0].video && - this.videos[0].video.currentTime > 0 && - this.videos[0].video.paused === false && - this.videos[0].video.ended === false && - this.videos[0].video.readyState === 4 - ) { - this.ee.emitEvent('streamPlaying', [new StreamManagerEvent(this, 'streamPlaying', undefined)]); - } - } - if (this.stream.hasAudio) { - if (type === 'publisherStartSpeaking') { - this.stream.enableOnceHarkSpeakingEvent(); - } - if (type === 'publisherStopSpeaking') { - this.stream.enableOnceHarkStoppedSpeakingEvent(); - } - if (type === 'streamAudioVolumeChange') { - this.stream.enableOnceHarkVolumeChangeEvent(false); - } - } - return this; - } - - /** - * See {@link EventDispatcher.off} - */ - off(type: K, handler?: (event: StreamManagerEventMap[K]) => void): this { - super.offAux(type, handler); - - if (type === 'publisherStartSpeaking') { - // Both StreamManager and Session can have "publisherStartSpeaking" event listeners - const remainingStartSpeakingEventListeners = - this.ee.getListeners(type).length + this.stream.session.ee.getListeners(type).length; - if (remainingStartSpeakingEventListeners === 0) { - this.stream.disableHarkSpeakingEvent(false); - } - } - if (type === 'publisherStopSpeaking') { - // Both StreamManager and Session can have "publisherStopSpeaking" event listeners - const remainingStopSpeakingEventListeners = - this.ee.getListeners(type).length + this.stream.session.ee.getListeners(type).length; - if (remainingStopSpeakingEventListeners === 0) { - this.stream.disableHarkStoppedSpeakingEvent(false); - } - } - if (type === 'streamAudioVolumeChange') { - // Only StreamManager can have "streamAudioVolumeChange" event listeners - const remainingVolumeEventListeners = this.ee.getListeners(type).length; - if (remainingVolumeEventListeners === 0) { - this.stream.disableHarkVolumeChangeEvent(false); - } - } - - return this; - } - - /** - * Makes `video` element parameter display this {@link stream}. This is useful when you are - * [managing the video elements on your own](/en/stable/cheatsheet/manage-videos/#you-take-care-of-the-video-players) - * - * Calling this method with a video already added to other Publisher/Subscriber will cause the video element to be - * disassociated from that previous Publisher/Subscriber and to be associated to this one. - * - * @returns 1 if the video wasn't associated to any other Publisher/Subscriber and has been successfully added to this one. - * 0 if the video was already added to this Publisher/Subscriber. -1 if the video was previously associated to any other - * Publisher/Subscriber and has been successfully disassociated from that one and properly added to this one. - */ - addVideoElement(video: HTMLVideoElement): number { - this.initializeVideoProperties(video); - - if (!this.remote && this.stream.displayMyRemote()) { - if (video.srcObject !== this.stream.getMediaStream()) { - video.srcObject = this.stream.getMediaStream(); - } - } - - // If the video element is already part of this StreamManager do nothing - for (const v of this.videos) { - if (v.video === video) { - return 0; - } - } - - let returnNumber = 1; - - for (const streamManager of this.stream.session.streamManagers) { - if (streamManager.disassociateVideo(video)) { - returnNumber = -1; - break; - } - } - - this.stream.session.streamManagers.forEach((streamManager) => { - streamManager.disassociateVideo(video); - }); - - this.pushNewStreamManagerVideo({ - video, - id: video.id, - canplayListenerAdded: false - }); - - logger.info('New video element associated to ', this); - - return returnNumber; - } - - /** - * Creates a new video element displaying this {@link stream}. This allows you to have multiple video elements displaying the same media stream. - * - * #### Events dispatched - * - * The Publisher/Subscriber object will dispatch a `videoElementCreated` event once the HTML video element has been added to DOM. See {@link VideoElementEvent} - * - * @param targetElement HTML DOM element (or its `id` attribute) in which the video element of the Publisher/Subscriber will be inserted - * @param insertMode How the video element will be inserted accordingly to `targetElemet` - * - * @returns The created HTMLVideoElement - */ - createVideoElement(targetElement?: string | HTMLElement, insertMode?: VideoInsertMode): HTMLVideoElement { - let targEl; - if (typeof targetElement === 'string') { - targEl = document.getElementById(targetElement); - if (!targEl) { - throw new Error("The provided 'targetElement' couldn't be resolved to any HTML element: " + targetElement); - } - } else if (targetElement instanceof HTMLElement) { - targEl = targetElement; - } else { - throw new Error("The provided 'targetElement' couldn't be resolved to any HTML element: " + targetElement); - } - - const video = this.createVideo(); - this.initializeVideoProperties(video); - - let insMode = !!insertMode ? insertMode : VideoInsertMode.APPEND; - switch (insMode) { - case VideoInsertMode.AFTER: - targEl.parentNode!!.insertBefore(video, targEl.nextSibling); - break; - case VideoInsertMode.APPEND: - targEl.appendChild(video); - break; - case VideoInsertMode.BEFORE: - targEl.parentNode!!.insertBefore(video, targEl); - break; - case VideoInsertMode.PREPEND: - targEl.insertBefore(video, targEl.childNodes[0]); - break; - case VideoInsertMode.REPLACE: - targEl.parentNode!!.replaceChild(video, targEl); - break; - default: - insMode = VideoInsertMode.APPEND; - targEl.appendChild(video); - break; - } - - const v: StreamManagerVideo = { - targetElement: targEl, - video, - insertMode: insMode, - id: video.id, - canplayListenerAdded: false - }; - this.pushNewStreamManagerVideo(v); - - this.ee.emitEvent('videoElementCreated', [new VideoElementEvent(v.video, this, 'videoElementCreated')]); - this.lazyLaunchVideoElementCreatedEvent = !!this.firstVideoElement; - - return video; - } - - /** - * Updates the current configuration for the {@link PublisherSpeakingEvent} feature and the [StreamManagerEvent.streamAudioVolumeChange](/en/stable/api/openvidu-browser/classes/StreamManagerEvent.html) feature for this specific - * StreamManager audio stream, overriding the global options set with {@link OpenVidu.setAdvancedConfiguration}. This way you can customize the audio events options - * for each specific StreamManager and change them dynamically. - * - * @param publisherSpeakingEventsOptions New options to be applied to this StreamManager's audio stream. It is an object which includes the following optional properties: - * - `interval`: (number) how frequently the analyser polls the audio stream to check if speaking has started/stopped or audio volume has changed. Default **100** (ms) - * - `threshold`: (number) the volume at which _publisherStartSpeaking_, _publisherStopSpeaking_ events will be fired. Default **-50** (dB) - */ - updatePublisherSpeakingEventsOptions(publisherSpeakingEventsOptions: { interval?: number; threshold?: number }): void { - const currentHarkOptions = !!this.stream.harkOptions - ? this.stream.harkOptions - : this.stream.session.openvidu.advancedConfiguration.publisherSpeakingEventsOptions || {}; - const newInterval = - typeof publisherSpeakingEventsOptions.interval === 'number' - ? publisherSpeakingEventsOptions.interval - : typeof currentHarkOptions.interval === 'number' - ? currentHarkOptions.interval - : 100; - const newThreshold = - typeof publisherSpeakingEventsOptions.threshold === 'number' - ? publisherSpeakingEventsOptions.threshold - : typeof currentHarkOptions.threshold === 'number' - ? currentHarkOptions.threshold - : -50; - this.stream.harkOptions = { - interval: newInterval, - threshold: newThreshold - }; - if (!!this.stream.speechEvent) { - this.stream.speechEvent.setInterval(newInterval); - this.stream.speechEvent.setThreshold(newThreshold); - } - } - - /* Hidden methods */ - - /** - * @hidden - */ - initializeVideoProperties(video: HTMLVideoElement): void { - if (!(!this.remote && this.stream.displayMyRemote())) { - // Avoid setting the MediaStream into the srcObject if remote subscription before publishing - if (video.srcObject !== this.stream.getMediaStream()) { - // If srcObject already set don't do it again - video.srcObject = this.stream.getMediaStream(); - } - } - video.autoplay = true; - video.controls = false; - - if ( - platform.isSafariBrowser() || - (platform.isIPhoneOrIPad() && - (platform.isChromeMobileBrowser() || - platform.isEdgeMobileBrowser() || - platform.isOperaMobileBrowser() || - platform.isFirefoxMobileBrowser())) - ) { - video.playsInline = true; - } - - if (!video.id) { - video.id = (this.remote ? 'remote-' : 'local-') + 'video-' + this.stream.streamId; - // DEPRECATED property: assign once the property id if the user provided a valid targetElement - if (!this.id && !!this.targetElement) { - this.id = video.id; - } - } - - if (this.remote && this.isMirroredVideo(video)) { - // Subscriber video associated to a previously mirrored video element - this.removeMirrorVideo(video); - } else if (!this.remote && !this.stream.displayMyRemote()) { - // Publisher video - video.muted = true; - if (this.isMirroredVideo(video) && !this.stream.outboundStreamOpts.publisherProperties.mirror) { - // If the video was already rotated and now is set to not mirror - this.removeMirrorVideo(video); - } else if (this.stream.outboundStreamOpts.publisherProperties.mirror && !this.stream.isSendScreen()) { - // If the video is now set to mirror and is not screen share - this.mirrorVideo(video); - } - } - } - - /** - * @hidden - */ - removeAllVideos(): void { - for (let i = this.stream.session.streamManagers.length - 1; i >= 0; --i) { - if (this.stream.session.streamManagers[i] === this) { - this.stream.session.streamManagers.splice(i, 1); - } - } - - this.videos.forEach((streamManagerVideo) => { - // Remove oncanplay event listener (only OpenVidu browser listener, not the user ones) - if (!!streamManagerVideo.video && !!streamManagerVideo.video.removeEventListener) { - streamManagerVideo.video.removeEventListener('canplay', this.canPlayListener); - } - streamManagerVideo.canplayListenerAdded = false; - if (!!streamManagerVideo.targetElement) { - // Only remove from DOM videos created by OpenVidu Browser (those generated by passing a valid targetElement in OpenVidu.initPublisher - // and Session.subscribe or those created by StreamManager.createVideoElement). All this videos triggered a videoElementCreated event - streamManagerVideo.video.parentNode!.removeChild(streamManagerVideo.video); - this.ee.emitEvent('videoElementDestroyed', [ - new VideoElementEvent(streamManagerVideo.video, this, 'videoElementDestroyed') - ]); - } - // Remove srcObject from the video - this.removeSrcObject(streamManagerVideo); - // Remove from collection of videos every video managed by OpenVidu Browser - this.videos = this.videos.filter((v) => !v.targetElement); - }); - } - - /** - * @hidden - */ - disassociateVideo(video: HTMLVideoElement): boolean { - let disassociated = false; - for (let i = 0; i < this.videos.length; i++) { - if (this.videos[i].video === video) { - this.videos[i].video.removeEventListener('canplay', this.canPlayListener); - this.videos.splice(i, 1); - disassociated = true; - logger.info('Video element disassociated from ', this); - break; - } - } - return disassociated; - } - - /** - * @hidden - */ - addPlayEventToFirstVideo() { - if (!!this.videos[0] && !!this.videos[0].video && !this.videos[0].canplayListenerAdded) { - this.activateStreamPlayingEventExceptionTimeout(); - this.videos[0].video.addEventListener('canplay', this.canPlayListener); - this.videos[0].canplayListenerAdded = true; - } - } - - /** - * @hidden - */ - updateMediaStream(mediaStream: MediaStream) { - this.videos.forEach((streamManagerVideo) => { - streamManagerVideo.video.srcObject = mediaStream; - if (platform.isIonicIos()) { - // iOS Ionic. LIMITATION: must reinsert the video in the DOM for - // the media stream to be updated - const vParent = streamManagerVideo.video.parentElement; - const newVideo = streamManagerVideo.video; - vParent!!.replaceChild(newVideo, streamManagerVideo.video); - streamManagerVideo.video = newVideo; - } - }); - } - - /** - * @hidden - */ - emitEvent(type: string, eventArray: any[]): void { - this.ee.emitEvent(type, eventArray); - } - - /** - * @hidden - */ - createVideo(): HTMLVideoElement { - return document.createElement('video'); - } - - /** - * @hidden - */ - removeSrcObject(streamManagerVideo: StreamManagerVideo) { - streamManagerVideo.video.srcObject = null; - this.deactivateStreamPlayingEventExceptionTimeout(); - } - - /** - * @hidden - */ - abstract replaceTrackInMediaStream(track: MediaStreamTrack, updateLastConstraints: boolean): void; - - /* Private methods */ - - protected pushNewStreamManagerVideo(streamManagerVideo: StreamManagerVideo) { - this.videos.push(streamManagerVideo); - this.addPlayEventToFirstVideo(); - if (this.stream.session.streamManagers.indexOf(this) === -1) { - this.stream.session.streamManagers.push(this); - } - } - - private mirrorVideo(video: HTMLVideoElement): void { - if (!platform.isIonicIos()) { - video.style.transform = 'rotateY(180deg)'; - video.style.webkitTransform = 'rotateY(180deg)'; - } - } - - private removeMirrorVideo(video: HTMLVideoElement): void { - video.style.transform = 'unset'; - video.style.webkitTransform = 'unset'; - } - - private isMirroredVideo(video: HTMLVideoElement): boolean { - return video.style.transform === 'rotateY(180deg)' || video.style.webkitTransform === 'rotateY(180deg)'; - } - - private activateStreamPlayingEventExceptionTimeout() { - if (!this.remote) { - // ExceptionEvent NO_STREAM_PLAYING_EVENT is only for subscribers - return; - } - if (this.streamPlayingEventExceptionTimeout != null) { - // The timeout is already activated - return; - } - // Trigger ExceptionEvent NO_STREAM_PLAYING_EVENT if after timeout there is no 'canplay' event - const msTimeout = this.stream.session.openvidu.advancedConfiguration.noStreamPlayingEventExceptionTimeout || 4000; - this.streamPlayingEventExceptionTimeout = setTimeout(() => { - const msg = - 'StreamManager of Stream ' + - this.stream.streamId + - ' (' + - (this.remote ? 'Subscriber' : 'Publisher') + - ') did not trigger "streamPlaying" event in ' + - msTimeout + - ' ms'; - logger.warn(msg); - this.stream.session.emitEvent('exception', [ - new ExceptionEvent(this.stream.session, ExceptionEventName.NO_STREAM_PLAYING_EVENT, (this) as Subscriber, msg) - ]); - delete this.streamPlayingEventExceptionTimeout; - }, msTimeout); - } - - private deactivateStreamPlayingEventExceptionTimeout() { - clearTimeout(this.streamPlayingEventExceptionTimeout as any); - delete this.streamPlayingEventExceptionTimeout; - } -} diff --git a/openvidu-browser/src/OpenVidu/Subscriber.ts b/openvidu-browser/src/OpenVidu/Subscriber.ts deleted file mode 100644 index a49c8068..00000000 --- a/openvidu-browser/src/OpenVidu/Subscriber.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Stream } from './Stream'; -import { StreamManager } from './StreamManager'; -import { SubscriberProperties } from '../OpenViduInternal/Interfaces/Public/SubscriberProperties'; -import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; - -/** - * @hidden - */ -const logger: OpenViduLogger = OpenViduLogger.getInstance(); - -/** - * Packs remote media streams. Participants automatically receive them when others publish their streams. Initialized with {@link Session.subscribe} method - * - * See available event listeners at {@link StreamManagerEventMap}. - */ -export class Subscriber extends StreamManager { - /** - * @hidden - */ - properties: SubscriberProperties; - - /** - * @hidden - */ - constructor(stream: Stream, targEl: string | HTMLElement | undefined, properties: SubscriberProperties) { - super(stream, targEl); - this.element = this.targetElement; - this.stream = stream; - this.properties = properties; - } - - /** - * Subscribe or unsubscribe from the audio stream (if available). Calling this method twice in a row passing same value will have no effect - * @param value `true` to subscribe to the audio stream, `false` to unsubscribe from it - */ - subscribeToAudio(value: boolean): Subscriber { - this.stream - .getMediaStream() - .getAudioTracks() - .forEach((track) => { - track.enabled = value; - }); - this.stream.audioActive = value; - logger.info("'Subscriber' has " + (value ? 'subscribed to' : 'unsubscribed from') + ' its audio stream'); - return this; - } - - /** - * Subscribe or unsubscribe from the video stream (if available). Calling this method twice in a row passing same value will have no effect - * @param value `true` to subscribe to the video stream, `false` to unsubscribe from it - */ - subscribeToVideo(value: boolean): Subscriber { - this.stream - .getMediaStream() - .getVideoTracks() - .forEach((track) => { - track.enabled = value; - }); - this.stream.videoActive = value; - logger.info("'Subscriber' has " + (value ? 'subscribed to' : 'unsubscribed from') + ' its video stream'); - return this; - } - - /* Hidden methods */ - - /** - * @hidden - */ - replaceTrackInMediaStream(track: MediaStreamTrack, updateLastConstraints: boolean): void { - const mediaStream: MediaStream = this.stream.getMediaStream(); - let removedTrack: MediaStreamTrack; - if (track.kind === 'video') { - removedTrack = mediaStream.getVideoTracks()[0]; - if (updateLastConstraints) { - this.stream.lastVideoTrackConstraints = track.getConstraints(); - } - } else { - removedTrack = mediaStream.getAudioTracks()[0]; - } - mediaStream.removeTrack(removedTrack); - removedTrack.stop(); - mediaStream.addTrack(track); - } -} diff --git a/openvidu-browser/src/OpenVidu/tsconfig.json b/openvidu-browser/src/OpenVidu/tsconfig.json deleted file mode 100644 index ff476291..00000000 --- a/openvidu-browser/src/OpenVidu/tsconfig.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - //"allowUnusedLabels": true, - "allowUnreachableCode": false, - "buildOnSave": false, - "compileOnSave": true, - "compilerOptions": { - "allowJs": true, - "allowSyntheticDefaultImports": true, - "emitBOM": false, - "forceConsistentCasingInFileNames": true, - "lib": [ - "dom", - "es2015.promise", - "es5", - "scripthost" - ], - "module": "commonjs", - "noFallthroughCasesInSwitch": true, - //"noImplicitAny": true, - "noImplicitReturns": true, - "noImplicitThis": true, - //"noUnusedLocals": true, - //"noUnusedParameters": true, - "outDir": "../../lib", - "preserveConstEnums": true, - "removeComments": true, - "skipDefaultLibCheck": true, - "skipLibCheck": true, - "sourceMap": true, - "strictNullChecks": true, - "suppressExcessPropertyErrors": true, - "suppressImplicitAnyIndexErrors": true, - "target": "es5" - } -} \ No newline at end of file diff --git a/openvidu-browser/src/OpenViduInternal/Enums/LocalRecorderState.ts b/openvidu-browser/src/OpenViduInternal/Enums/LocalRecorderState.ts deleted file mode 100644 index ac815d7a..00000000 --- a/openvidu-browser/src/OpenViduInternal/Enums/LocalRecorderState.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -export enum LocalRecorderState { - READY = 'READY', - RECORDING = 'RECORDING', - PAUSED = 'PAUSED', - FINISHED = 'FINISHED' -} diff --git a/openvidu-browser/src/OpenViduInternal/Enums/OpenViduError.ts b/openvidu-browser/src/OpenViduInternal/Enums/OpenViduError.ts deleted file mode 100644 index a3b08e2c..00000000 --- a/openvidu-browser/src/OpenViduInternal/Enums/OpenViduError.ts +++ /dev/null @@ -1,141 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/** - * Defines property {@link OpenViduError.name} - */ -export enum OpenViduErrorName { - /** - * Browser is not supported by OpenVidu. - * Returned upon unsuccessful {@link Session.connect} - */ - BROWSER_NOT_SUPPORTED = 'BROWSER_NOT_SUPPORTED', - - /** - * The user hasn't granted permissions to the required input device when the browser asked for them. - * Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia} - */ - DEVICE_ACCESS_DENIED = 'DEVICE_ACCESS_DENIED', - - /** - * The required input device is probably being used by other process when the browser asked for it. - * This error can also be triggered when the user granted permission to use the devices but a hardware - * error occurred at the OS, browser or web page level, which prevented access to the device. - * Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia} - */ - DEVICE_ALREADY_IN_USE = 'DEVICE_ALREADY_IN_USE', - - /** - * The user hasn't granted permissions to capture some desktop screen when the browser asked for them. - * Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia} - */ - SCREEN_CAPTURE_DENIED = 'SCREEN_CAPTURE_DENIED', - - /** - * Browser does not support screen sharing. - * Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia} - */ - SCREEN_SHARING_NOT_SUPPORTED = 'SCREEN_SHARING_NOT_SUPPORTED', - - /** - * Only for Chrome, there's no screen sharing extension installed - * Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia} - */ - SCREEN_EXTENSION_NOT_INSTALLED = 'SCREEN_EXTENSION_NOT_INSTALLED', - - /** - * Only for Chrome, the screen sharing extension is installed but is disabled - * Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia} - */ - SCREEN_EXTENSION_DISABLED = 'SCREEN_EXTENSION_DISABLED', - - /** - * No video input device found with the provided deviceId (property {@link PublisherProperties.videoSource}) - * Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia} - */ - INPUT_VIDEO_DEVICE_NOT_FOUND = 'INPUT_VIDEO_DEVICE_NOT_FOUND', - - /** - * No audio input device found with the provided deviceId (property {@link PublisherProperties.audioSource}) - * Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia} - */ - INPUT_AUDIO_DEVICE_NOT_FOUND = 'INPUT_AUDIO_DEVICE_NOT_FOUND', - - /** - * There was an unknown error when trying to access the specified audio device - * Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia} - */ - INPUT_AUDIO_DEVICE_GENERIC_ERROR = 'INPUT_AUDIO_DEVICE_GENERIC_ERROR', - - /** - * Method {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia} has been called with properties `videoSource` and `audioSource` of - * {@link PublisherProperties} parameter both set to *false* or *null* - */ - NO_INPUT_SOURCE_SET = 'NO_INPUT_SOURCE_SET', - - /** - * Some media property of {@link PublisherProperties} such as `frameRate` or `resolution` is not supported - * by the input devices (whenever it is possible they are automatically adjusted to the most similar value). - * Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia} - */ - PUBLISHER_PROPERTIES_ERROR = 'PUBLISHER_PROPERTIES_ERROR', - - /** - * The client tried to call a method without the required permissions. This can occur for methods {@link Session.publish}, - * {@link Session.forceUnpublish}, {@link Session.forceDisconnect}, {@link Stream.applyFilter}, {@link Stream.removeFilter} - */ - OPENVIDU_PERMISSION_DENIED = 'OPENVIDU_PERMISSION_DENIED', - - /** - * There is no connection to the Session. This error will be thrown when any method requiring a connection to - * openvidu-server is called before successfully calling method {@link Session.connect} - */ - OPENVIDU_NOT_CONNECTED = 'OPENVIDU_NOT_CONNECTED', - - /** - * Error related to [Virtual Background](/en/stable/advanced-features/virtual-background/) - */ - VIRTUAL_BACKGROUND_ERROR = 'VIRTUAL_BACKGROUND_ERROR', - - /** - * Generic error - */ - GENERIC_ERROR = 'GENERIC_ERROR' -} - -/** - * Simple object to identify runtime errors on the client side - */ -export class OpenViduError { - /** - * Uniquely identifying name of the error - */ - name: OpenViduErrorName; - - /** - * Full description of the error - */ - message: string; - - /** - * @hidden - */ - constructor(name: OpenViduErrorName, message: string) { - this.name = name; - this.message = message; - } -} diff --git a/openvidu-browser/src/OpenViduInternal/Enums/TypeOfVideo.ts b/openvidu-browser/src/OpenViduInternal/Enums/TypeOfVideo.ts deleted file mode 100644 index 863d911b..00000000 --- a/openvidu-browser/src/OpenViduInternal/Enums/TypeOfVideo.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -export enum TypeOfVideo { - CAMERA = 'CAMERA', - SCREEN = 'SCREEN', - CUSTOM = 'CUSTOM', - IPCAM = 'IPCAM' -} diff --git a/openvidu-browser/src/OpenViduInternal/Enums/VideoInsertMode.ts b/openvidu-browser/src/OpenViduInternal/Enums/VideoInsertMode.ts deleted file mode 100644 index 0aa16dc2..00000000 --- a/openvidu-browser/src/OpenViduInternal/Enums/VideoInsertMode.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/** - * How the video will be inserted in the DOM for Publishers and Subscribers. See {@link PublisherProperties.insertMode} and {@link SubscriberProperties.insertMode} - */ -export enum VideoInsertMode { - /** - * Video inserted after the target element (as next sibling) - */ - AFTER = 'AFTER', - /** - * Video inserted as last child of the target element - */ - APPEND = 'APPEND', - /** - * Video inserted before the target element (as previous sibling) - */ - BEFORE = 'BEFORE', - /** - * Video inserted as first child of the target element - */ - PREPEND = 'PREPEND', - /** - * Video replaces target element - */ - REPLACE = 'REPLACE' -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/ConnectionEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/ConnectionEvent.ts deleted file mode 100644 index b32983f1..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/ConnectionEvent.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Event } from './Event'; -import { Connection } from '../../OpenVidu/Connection'; -import { Session } from '../../OpenVidu/Session'; -import { ConnectionEventReason } from './Types/Types'; - -/** - * Triggered by: - * - {@link SessionEventMap.connectionCreated} - * - {@link SessionEventMap.connectionDestroyed} - */ -export class ConnectionEvent extends Event { - /** - * Connection object that was created or destroyed - */ - connection: Connection; - - /** - * For `connectionDestroyed` event: - * - "disconnect": the remote user has called `Session.disconnect()` - * - "forceDisconnectByUser": the remote user has been evicted from the Session by other user calling `Session.forceDisconnect()` - * - "forceDisconnectByServer": the remote user has been evicted from the Session by the application - * - "sessionClosedByServer": the Session has been closed by the application - * - "networkDisconnect": the remote user network connection has dropped - * - "nodeCrashed": a node has crashed in the server side - * - * For `connectionCreated` event an empty string - */ - reason: ConnectionEventReason; - - /** - * @hidden - */ - constructor(cancelable: boolean, target: Session, type: string, connection: Connection, reason: ConnectionEventReason) { - super(cancelable, target, type); - this.connection = connection; - this.reason = reason; - } - - /** - * @hidden - */ - // tslint:disable-next-line:no-empty - callDefaultBehavior() { } -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/ConnectionPropertyChangedEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/ConnectionPropertyChangedEvent.ts deleted file mode 100644 index 42d328ab..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/ConnectionPropertyChangedEvent.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Connection } from '../../OpenVidu/Connection'; -import { Session } from '../../OpenVidu/Session'; -import { Event } from './Event'; - -/** - * **This feature is part of OpenVidu - * PRO - * and - * ENTERPRISE - * editions** - * - * Triggered by {@link SessionEventMap.connectionPropertyChanged} - */ -export class ConnectionPropertyChangedEvent extends Event { - /** - * The Connection whose property has changed - */ - connection: Connection; - - /** - * The property of the stream that changed. This value is either `"role"` or `"record"` - */ - changedProperty: string; - - /** - * New value of the property (after change, current value) - */ - newValue: Object; - - /** - * Previous value of the property (before change) - */ - oldValue: Object; - - /** - * @hidden - */ - constructor(target: Session, connection: Connection, changedProperty: string, newValue: Object, oldValue: Object) { - super(false, target, 'connectionPropertyChanged'); - this.connection = connection; - this.changedProperty = changedProperty; - this.newValue = newValue; - this.oldValue = oldValue; - } - - /** - * @hidden - */ - // tslint:disable-next-line:no-empty - callDefaultBehavior() {} -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/Event.ts b/openvidu-browser/src/OpenViduInternal/Events/Event.ts deleted file mode 100644 index f19b7e38..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/Event.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Filter } from '../../OpenVidu/Filter'; -import { StreamManager } from '../../OpenVidu/StreamManager'; -import { Session } from '../../OpenVidu/Session'; - -export abstract class Event { - /** - * Whether the event has a default behavior that may be prevented by calling {@link Event.preventDefault} - */ - cancelable: boolean; - - /** - * The object that dispatched the event - */ - target: Session | StreamManager | Filter; - - /** - * The type of event. This is the same string you pass as first parameter when calling method `on()` of any object implementing {@link EventDispatcher} interface - */ - type: string; - - /** - * @hidden - */ - hasBeenPrevented = false; - - /** - * @hidden - */ - constructor(cancelable: boolean, target: Session | StreamManager | Filter, type: string) { - this.cancelable = cancelable; - this.target = target; - this.type = type; - } - - /** - * Whether the default beahivour of the event has been prevented or not. Call {@link Event.preventDefault} to prevent it - */ - isDefaultPrevented(): boolean { - return this.hasBeenPrevented; - } - - /** - * Prevents the default behavior of the event. The following events have a default behavior: - * - * - `sessionDisconnected`: dispatched by {@link Session} object, automatically unsubscribes the leaving participant from every Subscriber object of the session (this includes closing the RTCPeerConnection and disposing all MediaStreamTracks) - * and also deletes any HTML video element associated to each Subscriber (only those created by OpenVidu Browser, either by passing a valid parameter as `targetElement` in method {@link Session.subscribe} or - * by calling {@link Subscriber.createVideoElement}). For every video removed, each Subscriber object will also dispatch a `videoElementDestroyed` event. - * - * - `streamDestroyed`: - * - If dispatched by a {@link Publisher} (*you* have unpublished): automatically stops all media tracks and deletes any HTML video element associated to it (only those created by OpenVidu Browser, either by passing a valid parameter as `targetElement` - * in method {@link OpenVidu.initPublisher} or by calling {@link Publisher.createVideoElement}). For every video removed, the Publisher object will also dispatch a `videoElementDestroyed` event. - * - If dispatched by {@link Session} (*other user* has unpublished): automatically unsubscribes the proper Subscriber object from the session (this includes closing the RTCPeerConnection and disposing all MediaStreamTracks) - * and also deletes any HTML video element associated to that Subscriber (only those created by OpenVidu Browser, either by passing a valid parameter as `targetElement` in method {@link Session.subscribe} or - * by calling {@link Subscriber.createVideoElement}). For every video removed, the Subscriber object will also dispatch a `videoElementDestroyed` event. - */ - preventDefault() { - // tslint:disable-next-line:no-empty - this.callDefaultBehavior = () => {}; - this.hasBeenPrevented = true; - } - - /** - * @hidden - */ - abstract callDefaultBehavior(); -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/EventMap/EventMap.ts b/openvidu-browser/src/OpenViduInternal/Events/EventMap/EventMap.ts deleted file mode 100644 index 3a74210e..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/EventMap/EventMap.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/** - * All OpenVidu Browser events inherit from this interface - */ -export interface EventMap {} diff --git a/openvidu-browser/src/OpenViduInternal/Events/EventMap/PublisherEventMap.ts b/openvidu-browser/src/OpenViduInternal/Events/EventMap/PublisherEventMap.ts deleted file mode 100644 index acae11cc..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/EventMap/PublisherEventMap.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { StreamEvent } from '../StreamEvent'; -import { StreamManagerEventMap } from './StreamManagerEventMap'; - -/** - * Events dispatched by {@link Publisher} object. Manage event listeners with - * {@link Publisher.on}, {@link Publisher.once} and {@link Publisher.off} methods. - * - * Example: - * - * ```javascript - * publisher.on('accessDenied', () => { - * console.error('Camera access has been denied!'); - * } - * - * publisher.off('accessDenied'); - * ``` - */ -export interface PublisherEventMap extends StreamManagerEventMap { - /** - * Event dispatched when the {@link Publisher} has been published to the session (see {@link Session.publish}). - */ - streamCreated: StreamEvent; - - /** - * Event dispatched when the {@link Publisher} has been unpublished from the session. - */ - streamDestroyed: StreamEvent; - - /** - * Event dispatched when a Publisher tries to access some media input device and has the required permissions to do so. - * - * This happens when calling {@link OpenVidu.initPublisher} or {@link OpenVidu.initPublisherAsync} and the application - * has permissions to use the devices. This usually means the user has accepted the permissions dialog that the - * browser will show when trying to access the camera/microphone/screen. - */ - accessAllowed: never; - - /** - * Event dispatched when a Publisher tries to access some media input device and does NOT have the required permissions to do so. - * - * This happens when calling {@link OpenVidu.initPublisher} or {@link OpenVidu.initPublisherAsync} and the application - * lacks the required permissions to use the devices. This usually means the user has NOT accepted the permissions dialog that the - * browser will show when trying to access the camera/microphone/screen. - */ - accessDenied: never; - - /** - * Event dispatched when the pop-up shown by the browser to request permissions for the input media devices is opened. - * - * You can use this event to alert the user about granting permissions for your website. Note that this event is artificially - * generated based only on time intervals when accessing media devices. A heavily overloaded client device that simply takes more - * than usual to access the media device could produce a false trigger of this event. - */ - accessDialogOpened: never; - - /** - * Event dispatched after the user clicks on "Allow" or "Block" in the pop-up shown by the browser to request permissions - * for the input media devices. - * - * This event can only be triggered after an {@link accessDialogOpened} event has been previously triggered. - */ - accessDialogClosed: never; -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/EventMap/SessionEventMap.ts b/openvidu-browser/src/OpenViduInternal/Events/EventMap/SessionEventMap.ts deleted file mode 100644 index 0e7f76a3..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/EventMap/SessionEventMap.ts +++ /dev/null @@ -1,225 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { EventMap } from './EventMap'; -import { ConnectionEvent } from '../ConnectionEvent'; -import { ConnectionPropertyChangedEvent } from '../ConnectionPropertyChangedEvent'; -import { ExceptionEvent } from '../ExceptionEvent'; -import { NetworkQualityLevelChangedEvent } from '../NetworkQualityLevelChangedEvent'; -import { PublisherSpeakingEvent } from '../PublisherSpeakingEvent'; -import { RecordingEvent } from '../RecordingEvent'; -import { SessionDisconnectedEvent } from '../SessionDisconnectedEvent'; -import { SignalEvent } from '../SignalEvent'; -import { SpeechToTextEvent } from '../SpeechToTextEvent'; -import { StreamEvent } from '../StreamEvent'; -import { StreamPropertyChangedEvent } from '../StreamPropertyChangedEvent'; - -/** - * Events dispatched by {@link Session} object. Manage event listeners with - * {@link Session.on}, {@link Session.once} and {@link Session.off} methods. - * - * Example: - * - * ```javascript - * session.on('connectionCreated', (event) => { - * console.log('Connection ' + event.connection.connectionId + ' created'); - * } - * - * session.off('connectionDestroyed'); - * ``` - */ -export interface SessionEventMap extends EventMap { - /** - * Event dispatched when a new user has connected to the session. - * - * It is fired for both the local user and remote users. - */ - connectionCreated: ConnectionEvent; - - /** - * Event dispatched when a remote user has left the session. - * - * For the local user see {@link sessionDisconnected}. - */ - connectionDestroyed: ConnectionEvent; - - /** - * **This feature is part of OpenVidu - * PRO - * and - * ENTERPRISE - * editions** - * - * Event dispatched when a property of the local {@link Connection} object changes. - * - * It is fired only for the local user. - * - * The properties that may change are {@link Connection.role} and {@link Connection.record}. - * The only way the Connection properties may change is by updating them through: - * - * - [API REST](/en/stable/reference-docs/REST-API/#patch-connection) - * - [openvidu-java-client](/en/stable/reference-docs/openvidu-java-client/#update-a-connection) - * - [openvidu-node-client](/en/stable/reference-docs/openvidu-node-client/#update-a-connection)

- */ - connectionPropertyChanged: ConnectionPropertyChangedEvent; - - /** - * Event dispatched when the local user has left the session. - * - * For remote users see {@link connectionDestroyed}. - */ - sessionDisconnected: SessionDisconnectedEvent; - - /** - * Event dispatched when a user has started publishing media to the session (see {@link Session.publish}). - * - * It is fired for both the local user and remote users. - */ - streamCreated: StreamEvent; - - /** - * Event dispatched when a user stops publishing media to the session. - * - * It is fired for both the local user and remote users. - */ - streamDestroyed: StreamEvent; - - /** - * Event dispatched when a Stream undergoes any change in any of its mutable properties - * (see {@link StreamPropertyChangedEvent.changedProperty}). - * - * It is fired for both remote streams (owned by a {@link Subscriber}) or local streams (owned by a {@link Publisher}). - */ - streamPropertyChanged: StreamPropertyChangedEvent; - - /** - * Event dispatched when a user has started speaking. - * - * It is fired for both the local user and remote users. - * - * Extra information: - * - This event will only be triggered for **streams that have audio tracks** ({@link Stream.hasAudio} must be true). - * - Further configuration can be applied on how the event is dispatched by setting property `publisherSpeakingEventsOptions` in the call of {@link OpenVidu.setAdvancedConfiguration}. - */ - publisherStartSpeaking: PublisherSpeakingEvent; - - /** - * Event dispatched when a user has stopped speaking. - * - * It is fired for both the local user and remote users. - * - * Extra information: - * - This event will only be triggered for **streams that have audio tracks** ({@link Stream.hasAudio} must be true). - * - Further configuration can be applied on how the event is dispatched by setting property `publisherSpeakingEventsOptions` in the call of {@link OpenVidu.setAdvancedConfiguration}. - */ - publisherStopSpeaking: PublisherSpeakingEvent; - - /** - * @hidden - */ - [key: `signal:${string}`]: SignalEvent; - - /** - * Event dispatched when a signal is received (see [Send text messages between users](/en/stable/cheatsheet/send-messages)). - * - * If the listener is added as **`signal:TYPE`**, only signals of type **`TYPE`** will trigger the event. - */ - signal: SignalEvent; - - /** - * Event dispatched when the session has started being recorded. - * - * Property **`OPENVIDU_RECORDING_NOTIFICATION`** of [the OpenVidu deployment configuration](/en/stable/reference-docs/openvidu-config/) - * defines which users should receive this events (by default, only users with role `PUBLISHER` or `MODERATOR`) - */ - recordingStarted: RecordingEvent; - - /** - * Event dispatched when the session has stopped being recorded. - * - * Property **`OPENVIDU_RECORDING_NOTIFICATION`** of [the OpenVidu deployment configuration](/en/stable/reference-docs/openvidu-config/) - * defines which users should receive this events (by default, only users with role `PUBLISHER` or `MODERATOR`) - */ - recordingStopped: RecordingEvent; - - /** - * **This feature is part of OpenVidu - * PRO - * and - * ENTERPRISE - * editions** - * - * Event dispatched when the session has started being broadcasted. See [Broadcast to YouTube/Twitch](/en/stable/advanced-features/broadcast/) - */ - broadcastStarted: never; - - /** - * **This feature is part of OpenVidu - * PRO - * and - * ENTERPRISE - * editions** - * - * Event dispatched when the session has stopped being broadcasted. See [Broadcast to YouTube/Twitch](/en/stable/advanced-features/broadcast/) - */ - broadcastStopped: never; - - /** - * **This feature is part of OpenVidu - * PRO - * and - * ENTERPRISE - * editions** - * - * Event dispatched when the network quality level of a {@link Connection} changes. See [network quality](/en/stable/advanced-features/network-quality/). - */ - networkQualityLevelChanged: NetworkQualityLevelChangedEvent; - - /** - * **This feature is part of OpenVidu - * PRO - * and - * ENTERPRISE - * editions** - * - * Event dispatched when a speech-to-text message has been received for certain Stream. See [Speech To Text](/en/stable/advanced-features/speech-to-text/). - */ - speechToTextMessage: SpeechToTextEvent; - - /** - * Event dispatched when the local user has lost its connection to the session, and starts the automatic reconnection process. - * - * See [Reconnection events](/en/stable/advanced-features/automatic-reconnection/#reconnection-events). - */ - reconnecting: never; - - /** - * Event dispatched when the local user has successfully recovered its connection to the session after losing it. - * - * If the connection was recovered but OpenVidu Server already evicted the user due to timeout, then this event will - * not be dispatched. A {@link sessionDisconnected} event with reason `networkDisconnect` will be triggered instead. - * - * See [Reconnection events](/en/stable/advanced-features/automatic-reconnection/#reconnection-events). - */ - reconnected: never; - - /** - * This event acts as a global handler for asynchronous errors that may be triggered for multiple reasons and from multiple origins. - * To see the different types of exceptions go to {@link ExceptionEventName}. - */ - exception: ExceptionEvent; -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/EventMap/StreamManagerEventMap.ts b/openvidu-browser/src/OpenViduInternal/Events/EventMap/StreamManagerEventMap.ts deleted file mode 100644 index 47b78d08..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/EventMap/StreamManagerEventMap.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { EventMap } from './EventMap'; -import { PublisherSpeakingEvent } from '../PublisherSpeakingEvent'; -import { StreamManagerEvent } from '../StreamManagerEvent'; -import { StreamPropertyChangedEvent } from '../StreamPropertyChangedEvent'; -import { VideoElementEvent } from '../VideoElementEvent'; - -/** - * Events dispatched by {@link StreamManager} object. Manage event listeners with - * {@link StreamManager.on}, {@link StreamManager.once} and {@link StreamManager.off} methods. - * - * Example: - * - * ```javascript - * streamManager.on('videoElementCreated', (event) => { - * console.log('New video element created:', event.element); - * } - * - * streamManager.off('videoElementCreated'); - * ``` - */ -export interface StreamManagerEventMap extends EventMap { - /** - * Event dispatched when a new HTML video element has been inserted into DOM by OpenVidu Browser library. See - * [Manage video players](/en/stable/cheatsheet/manage-videos) section. - */ - videoElementCreated: VideoElementEvent; - - /** - * Event dispatched when an HTML video element has been removed from DOM by OpenVidu Browser library. See - * [Manage video players](/en/stable/cheatsheet/manage-videos) section. - */ - videoElementDestroyed: VideoElementEvent; - - /** - * Event dispatched when the media stream starts playing (one of its videos has media and has begun to play). - * This event will be dispatched when these 3 conditions are met: - * 1. The StreamManager has no video associated in the DOM. - * 2. It is associated to one video. - * 3. That video starts playing. Internally the expected Web API event is [HTMLMediaElement.canplay](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplay_event). - */ - streamPlaying: StreamManagerEvent; - - /** - * Event dispatched when the volume of the media stream's audio track changes. Only applies if {@link Stream.hasAudio} is `true`. - * The frequency this event is fired with is defined by property `interval` of - * {@link OpenViduAdvancedConfiguration.publisherSpeakingEventsOptions} (default 100ms) - */ - streamAudioVolumeChange: StreamManagerEvent; - - /** - * Event dispatched when a Stream undergoes any change in any of its mutable properties - * (see {@link StreamPropertyChangedEvent.changedProperty}). - */ - streamPropertyChanged: StreamPropertyChangedEvent; - - /** - * Event dispatched when the user owning the stream has started speaking. - * - * Extra information: - * - This event will only be triggered for **streams that have audio tracks** ({@link Stream.hasAudio} must be true). - * - Further configuration can be applied on how the event is dispatched by setting property `publisherSpeakingEventsOptions` in the call of {@link OpenVidu.setAdvancedConfiguration}. - */ - publisherStartSpeaking: PublisherSpeakingEvent; - - /** - * Event dispatched when the user owning the stream has stopped speaking. - * - * Extra information: - * - This event will only be triggered for **streams that have audio tracks** ({@link Stream.hasAudio} must be true). - * - Further configuration can be applied on how the event is dispatched by setting property `publisherSpeakingEventsOptions` in the call of {@link OpenVidu.setAdvancedConfiguration}. - */ - publisherStopSpeaking: PublisherSpeakingEvent; -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/ExceptionEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/ExceptionEvent.ts deleted file mode 100644 index e807b452..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/ExceptionEvent.ts +++ /dev/null @@ -1,136 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Session } from '../../OpenVidu/Session'; -import { Stream } from '../../OpenVidu/Stream'; -import { Subscriber } from '../../OpenVidu/Subscriber'; -import { Event } from './Event'; - -/** - * Defines property {@link ExceptionEvent.name} - */ -export enum ExceptionEventName { - /** - * There was an unexpected error on the server-side processing an ICE candidate generated and sent by the client-side. - * - * {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Session} object. - */ - ICE_CANDIDATE_ERROR = 'ICE_CANDIDATE_ERROR', - - /** - * The [ICE connection state](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceConnectionState) - * of an [RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection) reached `failed` status. - * - * This is a terminal error that won't have any kind of possible recovery. If the client is still connected to OpenVidu Server, - * then an automatic reconnection process of the media stream is immediately performed. If the ICE connection has broken due to - * a total network drop, then no automatic reconnection process will be possible. - * - * {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Stream} object. - */ - ICE_CONNECTION_FAILED = 'ICE_CONNECTION_FAILED', - - /** - * The [ICE connection state](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceConnectionState) - * of an [RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection) reached `disconnected` status. - * - * This is not a terminal error, and it is possible for the ICE connection to be reconnected. If the client is still connected to - * OpenVidu Server and after certain timeout the ICE connection has not reached a success or terminal status, then an automatic - * reconnection process of the media stream is performed. If the ICE connection has broken due to a total network drop, then no - * automatic reconnection process will be possible. - * - * You can customize the timeout for the reconnection attempt with property {@link OpenViduAdvancedConfiguration.iceConnectionDisconnectedExceptionTimeout}, - * which by default is 4000 milliseconds. - * - * {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Stream} object. - */ - ICE_CONNECTION_DISCONNECTED = 'ICE_CONNECTION_DISCONNECTED', - - /** - * A {@link Subscriber} object has not fired event `streamPlaying` after certain timeout. `streamPlaying` event belongs to {@link StreamManagerEvent} - * category. It wraps Web API native event [canplay](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplay_event). - * - * OpenVidu Browser can take care of the video players (see [here](/en/stable/cheatsheet/manage-videos/#let-openvidu-take-care-of-the-video-players)), - * or you can take care of video players on your own (see [here](/en/stable/cheatsheet/manage-videos/#you-take-care-of-the-video-players)). - * Either way, whenever a {@link Subscriber} object is commanded to attach its {@link Stream} to a video element, it is supposed to fire `streamPlaying` - * event shortly after. If it does not, then we can safely assume that something wrong has happened while playing the remote video and the - * application may be notified through this specific ExceptionEvent. - * - * The timeout can be configured with property {@link OpenViduAdvancedConfiguration.noStreamPlayingEventExceptionTimeout}. By default it is 4000 milliseconds. - * - * This is just an informative exception. It only means that a remote Stream that is supposed to be playing by a video player has not done so - * in a reasonable time. But the lack of the event can be caused by multiple reasons. If a Subscriber is not playing its Stream, the origin - * of the problem could be located at the Publisher side. Or may be caused by a transient network problem. But it also could be a problem with - * autoplay permissions. Bottom line, the cause can be very varied, and depending on the application the lack of the event could even be expected. - * - * {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Subscriber} object. - */ - NO_STREAM_PLAYING_EVENT = 'NO_STREAM_PLAYING_EVENT', - - /** - * There has been a server-side disconnection of the Speech To Text module. From the moment this exception is fired to the moment method - * {@link Session.subscribeToSpeechToText} is called again, the transcription of the audio stream will not be available and no {@link SpeechToTextEvent} - * will be fired. - * - * {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Session} object. - */ - SPEECH_TO_TEXT_DISCONNECTED = 'SPEECH_TO_TEXT_DISCONNECTED', -} - -/** - * Triggered by {@link SessionEventMap.exception} - */ -export class ExceptionEvent extends Event { - /** - * Name of the exception - */ - name: ExceptionEventName; - - /** - * Object affected by the exception. Depending on the {@link ExceptionEvent.name} property: - * - {@link Session}: `ICE_CANDIDATE_ERROR` - * - {@link Stream}: `ICE_CONNECTION_FAILED`, `ICE_CONNECTION_DISCONNECTED` - * - {@link Subscriber}: `NO_STREAM_PLAYING_EVENT` - */ - origin: Session | Stream | Subscriber; - - /** - * Informative description of the exception - */ - message: string; - - /** - * Any extra information associated to the exception - */ - data?: any; - - /** - * @hidden - */ - constructor(session: Session, name: ExceptionEventName, origin: Session | Stream | Subscriber, message: string, data?: any) { - super(false, session, 'exception'); - this.name = name; - this.origin = origin; - this.message = message; - this.data = data; - } - - /** - * @hidden - */ - // tslint:disable-next-line:no-empty - callDefaultBehavior() { } -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/FilterEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/FilterEvent.ts deleted file mode 100644 index 9b62c8eb..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/FilterEvent.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Event } from './Event'; -import { Filter } from '../../OpenVidu/Filter'; - -/** - * Defines every event dispatched by audio/video stream filters. You can subscribe to filter events by calling {@link Filter.addEventListener} - */ -export class FilterEvent extends Event { - /** - * Data of the event - */ - data: Object; - - /** - * @hidden - */ - constructor(target: Filter, eventType: string, data: Object) { - super(false, target, eventType); - this.data = data; - } - - /** - * @hidden - */ - // tslint:disable-next-line:no-empty - callDefaultBehavior() {} -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/NetworkQualityLevelChangedEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/NetworkQualityLevelChangedEvent.ts deleted file mode 100644 index 2c797e7c..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/NetworkQualityLevelChangedEvent.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Event } from './Event'; -import { Session } from '../../OpenVidu/Session'; -import { Connection } from '../../OpenVidu/Connection'; - -/** - * Triggered by {@link SessionEventMap.networkQualityLevelChanged} - */ -export class NetworkQualityLevelChangedEvent extends Event { - /** - * New value of the network quality level - */ - newValue: number; - - /** - * Old value of the network quality level - */ - oldValue: number; - - /** - * Connection for whom the network quality level changed - */ - connection: Connection; - - /** - * @hidden - */ - constructor(target: Session, newValue: number, oldValue: number, connection: Connection) { - super(false, target, 'networkQualityLevelChanged'); - this.newValue = newValue; - this.oldValue = oldValue; - this.connection = connection; - } - - /** - * @hidden - */ - // tslint:disable-next-line:no-empty - callDefaultBehavior() {} -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/PublisherSpeakingEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/PublisherSpeakingEvent.ts deleted file mode 100644 index b6be41fd..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/PublisherSpeakingEvent.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Event } from './Event'; -import { Connection } from '../../OpenVidu/Connection'; -import { Session } from '../../OpenVidu/Session'; -import { StreamManager } from '../../OpenVidu/StreamManager'; - -/** - * Triggered by: - * - `publisherStartSpeaking` (available for [Session](/en/stable/api/openvidu-browser/interfaces/SessionEventMap.html#publisherStartSpeaking) and [StreamManager](/en/stable/api/openvidu-browser/interfaces/StreamManagerEventMap.html#publisherStartSpeaking) objects) - * - `publisherStopSpeaking` (available for [Session](/en/stable/api/openvidu-browser/interfaces/SessionEventMap.html#publisherStopSpeaking) and [StreamManager](/en/stable/api/openvidu-browser/interfaces/StreamManagerEventMap.html#publisherStopSpeaking) objects) - */ -export class PublisherSpeakingEvent extends Event { - /** - * The client that started or stopped speaking - */ - connection: Connection; - - /** - * The streamId of the Stream affected by the speaking event - */ - streamId: string; - - /** - * @hidden - */ - constructor(target: Session | StreamManager, type: string, connection: Connection, streamId: string) { - super(false, target, type); - this.type = type; - this.connection = connection; - this.streamId = streamId; - } - - /** - * @hidden - */ - // tslint:disable-next-line:no-empty - callDefaultBehavior() {} -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/RecordingEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/RecordingEvent.ts deleted file mode 100644 index 4bd186dc..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/RecordingEvent.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Event } from './Event'; -import { Session } from '../../OpenVidu/Session'; -import { RecordingEventReason } from './Types/Types'; - -/** - * Triggered by: - * - {@link SessionEventMap.recordingStarted} - * - {@link SessionEventMap.recordingStopped} - */ -export class RecordingEvent extends Event { - /** - * The recording ID generated in openvidu-server - */ - id: string; - - /** - * The recording name you supplied to openvidu-server. For example, to name your recording file MY_RECORDING: - * - With **API REST**: POST to `/api/recordings/start` passing JSON body `{"session":"sessionId","name":"MY_RECORDING"}` - * - With **openvidu-java-client**: `OpenVidu.startRecording(sessionId, "MY_RECORDING")` or `OpenVidu.startRecording(sessionId, new RecordingProperties.Builder().name("MY_RECORDING").build())` - * - With **openvidu-node-client**: `OpenVidu.startRecording(sessionId, "MY_RECORDING")` or `OpenVidu.startRecording(sessionId, {name: "MY_RECORDING"})` - * - * If no name is supplied, this property will be undefined and the recorded file will be named after property {@link id} - */ - name?: string; - - /** - * For 'recordingStopped' event: - * - "recordingStoppedByServer": the recording has been gracefully stopped by the application - * - "sessionClosedByServer": the Session has been closed by the application - * - "automaticStop": see [Automatic stop of recordings](/en/stable/advanced-features/recording/#automatic-stop-of-recordings) - * - "nodeCrashed": a node has crashed in the server side - * - * For 'recordingStarted' empty string - */ - reason?: RecordingEventReason; - - /** - * @hidden - */ - constructor(target: Session, type: string, id: string, name: string, reason?: RecordingEventReason) { - super(false, target, type); - this.id = id; - if (name !== id) { - this.name = name; - } - this.reason = reason; - } - - /** - * @hidden - */ - // tslint:disable-next-line:no-empty - callDefaultBehavior() { } -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/SessionDisconnectedEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/SessionDisconnectedEvent.ts deleted file mode 100644 index 67c5dadb..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/SessionDisconnectedEvent.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Event } from './Event'; -import { Session } from '../../OpenVidu/Session'; -import { OpenViduLogger } from '../Logger/OpenViduLogger'; -import { ConnectionEventReason } from './Types/Types'; - -/** - * @hidden - */ -const logger: OpenViduLogger = OpenViduLogger.getInstance(); - -/** - * Triggered by {@link SessionEventMap.sessionDisconnected} - */ -export class SessionDisconnectedEvent extends Event { - /** - * - "disconnect": you have called `Session.disconnect()` - * - "forceDisconnectByUser": you have been evicted from the Session by other user calling `Session.forceDisconnect()` - * - "forceDisconnectByServer": you have been evicted from the Session by the application - * - "sessionClosedByServer": the Session has been closed by the application - * - "networkDisconnect": your network connection has dropped. Before a SessionDisconnectedEvent with this reason is triggered, - * Session object will always have previously dispatched a `reconnecting` event. If the reconnection process succeeds, - * Session object will dispatch a `reconnected` event. If it fails, Session object will dispatch a SessionDisconnectedEvent - * with reason "networkDisconnect" - * - "nodeCrashed": a node has crashed in the server side. You can use this reason to ask your application's backend to reconnect - * to a new session to replace the crashed one - */ - reason: ConnectionEventReason; - - /** - * @hidden - */ - constructor(target: Session, reason: ConnectionEventReason) { - super(true, target, 'sessionDisconnected'); - this.reason = reason; - } - - /** - * @hidden - */ - callDefaultBehavior() { - logger.info("Calling default behavior upon '" + this.type + "' event dispatched by 'Session'"); - - const session = this.target; - - // Dispose and delete all remote Connections - session.remoteConnections.forEach((remoteConnection) => { - const connectionId = remoteConnection.connectionId; - if (!!session.remoteConnections.get(connectionId)?.stream) { - session.remoteConnections.get(connectionId)?.stream!.disposeWebRtcPeer(); - session.remoteConnections.get(connectionId)?.stream!.disposeMediaStream(); - if (session.remoteConnections.get(connectionId)?.stream!.streamManager) { - session.remoteConnections.get(connectionId)?.stream!.streamManager.removeAllVideos(); - } - const streamId = session.remoteConnections.get(connectionId)?.stream?.streamId; - if (!!streamId) { - session.remoteStreamsCreated.delete(streamId); - } - session.remoteConnections.get(connectionId)?.dispose(); - } - session.remoteConnections.delete(connectionId); - }); - } -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/SignalEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/SignalEvent.ts deleted file mode 100644 index bc443b9a..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/SignalEvent.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Event } from './Event'; -import { Connection } from '../../OpenVidu/Connection'; -import { Session } from '../../OpenVidu/Session'; - -/** - * Triggered by {@link SessionEventMap.signal} - */ -export class SignalEvent extends Event { - /** - * The type of signal. It is string `"signal"` for those signals sent with no {@link SignalOptions.type} property, and `"signal:type"` if was sent with a - * valid {@link SignalOptions.type} property. - * - * The client must be specifically subscribed to `Session.on('signal:type', function(signalEvent) {...})` to trigger that type of signal. - * - * Subscribing to `Session.on('signal', function(signalEvent) {...})` will trigger all signals, no matter their type. - */ - type: string; - - /** - * The message of the signal (can be empty) - */ - data?: string; - - /** - * The client that sent the signal. This property is undefined if the signal - * was directly generated by the application server (not by other client) - */ - from?: Connection; - - /** - * @hidden - */ - constructor(target: Session, type?: string, data?: string, from?: Connection) { - super(false, target, 'signal'); - if (!!type) { - this.type = 'signal:' + type; - } - this.data = data; - this.from = from; - } - - /** - * @hidden - */ - // tslint:disable-next-line:no-empty - callDefaultBehavior() {} -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/SpeechToTextEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/SpeechToTextEvent.ts deleted file mode 100644 index fba138f9..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/SpeechToTextEvent.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Event } from './Event'; -import { Connection } from '../../OpenVidu/Connection'; -import { Session } from '../../OpenVidu/Session'; -import { SpeechToTextEventReason } from './Types/Types'; - -/** - * Triggered by {@link SessionEventMap.speechToTextMessage} - */ -export class SpeechToTextEvent extends Event { - - /** - * The {@link Connection} owning the Stream that produced the speech-to-text event. - * In other words, this is the participant that spoke and produced this transcription event. - */ - connection: Connection; - - /** - * The text of the event. This is the transcription for this specific piece of audio stream - */ - text: string; - - /** - * All speech-to-text events are generated - */ - reason: SpeechToTextEventReason; - - /** - * The original event from the speech to text engine. This can vary depending on the engine - */ - raw: string; - - /** - * [BCP-47](https://tools.ietf.org/html/bcp47) language tag (like "en-US" or "es-ES") of the recognized text. This will be the same as the language provided - * in method {@link Session.subscribeToSpeechToText} method - */ - lang: string; - - /** - * @hidden - */ - constructor(target: Session, connection: Connection, text: string, reason: SpeechToTextEventReason, raw: string, lang: string) { - super(false, target, 'speechToTextMessage'); - this.connection = connection; - this.text = text; - this.reason = reason; - this.raw = raw; - this.lang = lang; - } - - /** - * @hidden - */ - // tslint:disable-next-line:no-empty - callDefaultBehavior() { } -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/StreamEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/StreamEvent.ts deleted file mode 100644 index 7f447f4b..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/StreamEvent.ts +++ /dev/null @@ -1,113 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Event } from './Event'; -import { Publisher } from '../../OpenVidu/Publisher'; -import { Session } from '../../OpenVidu/Session'; -import { Stream } from '../../OpenVidu/Stream'; -import { OpenViduLogger } from '../Logger/OpenViduLogger'; -import { StreamEventReason } from './Types/Types'; - -/** - * @hidden - */ -const logger: OpenViduLogger = OpenViduLogger.getInstance(); - -/** - * Triggered by: - * - `streamCreated` (available for [Session](/en/stable/api/openvidu-browser/interfaces/SessionEventMap.html#streamCreated) and [Publisher](/en/stable/api/openvidu-browser/interfaces/PublisherEventMap.html#streamCreated) objects) - * - `streamDestroyed` (available for [Session](/en/stable/api/openvidu-browser/interfaces/SessionEventMap.html#streamDestroyed) and [Publisher](/en/stable/api/openvidu-browser/interfaces/PublisherEventMap.html#streamDestroyed) objects) - */ -export class StreamEvent extends Event { - /** - * Stream object that was created or destroyed - */ - stream: Stream; - - /** - * For 'streamDestroyed' event: - * - "unpublish": method `Session.unpublish()` has been called - * - "disconnect": method `Session.disconnect()` has been called - * - "forceUnpublishByUser": some user has called `Session.forceUnpublish()` over the Stream - * - "forceDisconnectByUser": some user has called `Session.forceDisconnect()` over the Stream - * - "forceUnpublishByServer": the user's stream has been unpublished from the Session by the application - * - "forceDisconnectByServer": the user has been evicted from the Session by the application - * - "sessionClosedByServer": the Session has been closed by the application - * - "networkDisconnect": the user's network connection has dropped - * - "nodeCrashed": a node has crashed in the server side - * - * For 'streamCreated' empty string - */ - reason: StreamEventReason; - - /** - * @hidden - */ - constructor(cancelable: boolean, target: Session | Publisher, type: string, stream: Stream, reason: StreamEventReason) { - super(cancelable, target, type); - this.stream = stream; - this.reason = reason; - } - - /** - * @hidden - */ - callDefaultBehavior() { - if (this.type === 'streamDestroyed') { - if (this.target instanceof Session) { - // Remote Stream - logger.info("Calling default behavior upon '" + this.type + "' event dispatched by 'Session'"); - this.stream.disposeWebRtcPeer(); - } else if (this.target instanceof Publisher) { - // Local Stream - logger.info("Calling default behavior upon '" + this.type + "' event dispatched by 'Publisher'"); - clearInterval((this.target).screenShareResizeInterval); - this.stream.isLocalStreamReadyToPublish = false; - - // Delete Publisher object from OpenVidu publishers array - const openviduPublishers = (this.target).openvidu.publishers; - for (let i = 0; i < openviduPublishers.length; i++) { - if (openviduPublishers[i] === this.target) { - openviduPublishers.splice(i, 1); - break; - } - } - } - - // Dispose the MediaStream local object - this.stream.disposeMediaStream(); - - // Remove from DOM all video elements associated to this Stream, if there's a StreamManager defined - // (method Session.subscribe must have been called) - if (this.stream.streamManager) this.stream.streamManager.removeAllVideos(); - - // Delete stream from Session.remoteStreamsCreated map - this.stream.session.remoteStreamsCreated.delete(this.stream.streamId); - - // Delete StreamOptionsServer from remote Connection - const remoteConnection = this.stream.session.remoteConnections.get(this.stream.connection.connectionId); - if (!!remoteConnection && !!remoteConnection.remoteOptions) { - const streamOptionsServer = remoteConnection.remoteOptions.streams; - for (let i = streamOptionsServer.length - 1; i >= 0; --i) { - if (streamOptionsServer[i].id === this.stream.streamId) { - streamOptionsServer.splice(i, 1); - } - } - } - } - } -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/StreamManagerEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/StreamManagerEvent.ts deleted file mode 100644 index 08924346..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/StreamManagerEvent.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Event } from './Event'; -import { StreamManager } from '../../OpenVidu/StreamManager'; - -/** - * Triggered by: - * - {@link StreamManagerEventMap.streamPlaying} - * - {@link StreamManagerEventMap.streamAudioVolumeChange} - */ -export class StreamManagerEvent extends Event { - /** - * For `streamAudioVolumeChange` event: - * - `{newValue: number, oldValue: number}`: new and old audio volume values. These values are between -100 (silence) and 0 (loudest possible volume). - * They are not exact and depend on how the browser is managing the audio track, but -100 and 0 can be taken as limit values. - * - * For `streamPlaying` event undefined - */ - value: Object | undefined; - - /** - * @hidden - */ - constructor(target: StreamManager, type: string, value: Object | undefined) { - super(false, target, type); - this.value = value; - } - - /** - * @hidden - */ - // tslint:disable-next-line:no-empty - callDefaultBehavior() {} -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/StreamPropertyChangedEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/StreamPropertyChangedEvent.ts deleted file mode 100644 index dc7dffeb..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/StreamPropertyChangedEvent.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Event } from './Event'; -import { Session } from '../../OpenVidu/Session'; -import { Stream } from '../../OpenVidu/Stream'; -import { StreamManager } from '../../OpenVidu/StreamManager'; -import { StreamPropertyChangedEventReason, ChangedPropertyType } from './Types/Types'; - -/** - * Triggered by `streamPropertyChanged` (available for [Session](/en/stable/api/openvidu-browser/interfaces/SessionEventMap.html#streamPropertyChanged) and [StreamManager](/en/stable/api/openvidu-browser/interfaces/StreamManagerEventMap.html#streamPropertyChanged) objects) - */ -export class StreamPropertyChangedEvent extends Event { - /** - * The Stream whose property has changed. You can always identify the user publishing the changed stream by consulting property {@link Stream.connection} - */ - stream: Stream; - - /** - * The property of the stream that changed. This value is either `"videoActive"`, `"audioActive"`, `"videoTrack"`, `"audioTrack"`, `"videoDimensions"` or `"filter"` - */ - changedProperty: ChangedPropertyType; - - /** - * Cause of the change on the stream's property: - * - For `videoActive`: `"publishVideo"` - * - For `audioActive`: `"publishAudio"` - * - For `videoTrack`: `"trackReplaced"` - * - For `audioTrack`: `"trackReplaced"` - * - For `videoDimensions`: `"deviceRotated"`, `"screenResized"` or `"trackReplaced"` - * - For `filter`: `"applyFilter"`, `"execFilterMethod"` or `"removeFilter"` - */ - reason: StreamPropertyChangedEventReason; - - /** - * New value of the property (after change, current value) - */ - newValue: Object; - - /** - * Previous value of the property (before change) - */ - oldValue: Object; - - /** - * @hidden - */ - constructor( - target: Session | StreamManager, - stream: Stream, - changedProperty: ChangedPropertyType, - newValue: Object, - oldValue: Object, - reason: StreamPropertyChangedEventReason - ) { - super(false, target, 'streamPropertyChanged'); - this.stream = stream; - this.changedProperty = changedProperty; - this.newValue = newValue; - this.oldValue = oldValue; - this.reason = reason; - } - - /** - * @hidden - */ - // tslint:disable-next-line:no-empty - callDefaultBehavior() { } -} diff --git a/openvidu-browser/src/OpenViduInternal/Events/Types/Types.ts b/openvidu-browser/src/OpenViduInternal/Events/Types/Types.ts deleted file mode 100644 index 30f0314f..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/Types/Types.ts +++ /dev/null @@ -1,42 +0,0 @@ -export type ChangedPropertyType = - 'videoActive' | - 'audioActive' | - 'videoTrack' | - 'audioTrack' | - 'videoDimensions' | - 'filter'; - -export type StreamPropertyChangedEventReason = - 'publishVideo' | - 'publishAudio' | - 'trackReplaced' | - 'deviceRotated' | - 'screenResized' | - 'applyFilter' | - 'execFilterMethod' | - 'removeFilter'; - -export type ConnectionEventReason = - 'disconnect' | - 'forceDisconnectByUser' | - 'forceDisconnectByServer' | - 'sessionClosedByServer' | - 'networkDisconnect' | - 'nodeCrashed' | - ''; - -export type StreamEventReason = - ConnectionEventReason | - 'unpublish' | - 'forceUnpublishByUser' | - 'forceUnpublishByServer'; - -export type RecordingEventReason = - 'recordingStoppedByServer' | - 'sessionClosedByServer' | - 'automaticStop' | - 'nodeCrashed'; - -export type SpeechToTextEventReason = - 'recognizing' | - 'recognized'; \ No newline at end of file diff --git a/openvidu-browser/src/OpenViduInternal/Events/VideoElementEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/VideoElementEvent.ts deleted file mode 100644 index 3617cc88..00000000 --- a/openvidu-browser/src/OpenViduInternal/Events/VideoElementEvent.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Event } from './Event'; -import { StreamManager } from '../../OpenVidu/StreamManager'; - -/** - * Triggered by: - * - {@link StreamManagerEventMap.videoElementCreated} - * - {@link StreamManagerEventMap.videoElementDestroyed} - */ -export class VideoElementEvent extends Event { - /** - * Video element that was created or destroyed - */ - element: HTMLVideoElement; - - /** - * @hidden - */ - constructor(element: HTMLVideoElement, target: StreamManager, type: string) { - super(false, target, type); - this.element = element; - } - - /** - * @hidden - */ - // tslint:disable-next-line:no-empty - callDefaultBehavior() {} -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/CustomMediaStreamConstraints.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/CustomMediaStreamConstraints.ts deleted file mode 100644 index cddadb1e..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/CustomMediaStreamConstraints.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -export interface CustomMediaStreamConstraints { - constraints: MediaStreamConstraints; - audioTrack: MediaStreamTrack | undefined; - videoTrack: MediaStreamTrack | undefined; -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/IceServerProperties.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/IceServerProperties.ts deleted file mode 100644 index 533e9a3e..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/IceServerProperties.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -export interface IceServerProperties { - url: string; - username?: string; - credential?: string; -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/InboundStreamOptions.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/InboundStreamOptions.ts deleted file mode 100644 index 0a9a724a..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/InboundStreamOptions.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Connection } from '../../../OpenVidu/Connection'; -import { Filter } from '../../../OpenVidu/Filter'; -import { TypeOfVideo } from '../../Enums/TypeOfVideo'; - -export interface InboundStreamOptions { - id: string; - createdAt: number; - connection: Connection; - hasAudio: boolean; - hasVideo: boolean; - audioActive: boolean; - videoActive: boolean; - typeOfVideo: TypeOfVideo; - frameRate: number; - videoDimensions: { width: number; height: number }; - filter?: Filter; -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/LocalConnectionOptions.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/LocalConnectionOptions.ts deleted file mode 100644 index ef5fc3ea..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/LocalConnectionOptions.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { RemoteConnectionOptions } from './RemoteConnectionOptions'; -import { IceServerProperties } from './IceServerProperties'; - -export interface LocalConnectionOptions { - id: string; - finalUserId: string; - createdAt: number; - metadata: string; - value: RemoteConnectionOptions[]; - session: string; // OpenVidu Session identifier - sessionId: string; // JSON-RPC session identifier - role: string; - record: boolean; - coturnIp: string; - coturnPort: number; - turnUsername: string; - turnCredential: string; - version: string; - mediaServer: string; - videoSimulcast: boolean; - life: number; - customIceServers?: IceServerProperties[]; - recordingId?: string; // Defined if the session is being recorded and the client must be notified - recordingName?: string; // Defined if the session is being recorded and the client must be notified -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/OutboundStreamOptions.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/OutboundStreamOptions.ts deleted file mode 100644 index e954c8b3..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/OutboundStreamOptions.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { PublisherProperties } from '../Public/PublisherProperties'; - -export interface OutboundStreamOptions { - publisherProperties: PublisherProperties; - mediaConstraints: MediaStreamConstraints; -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/RemoteConnectionOptions.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/RemoteConnectionOptions.ts deleted file mode 100644 index 33f0bb7a..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/RemoteConnectionOptions.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { StreamOptionsServer } from './StreamOptionsServer'; - -export interface RemoteConnectionOptions { - id: string; - createdAt: number; - metadata: string; - streams: StreamOptionsServer[]; -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/SessionOptions.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/SessionOptions.ts deleted file mode 100644 index efe276bc..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/SessionOptions.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -export interface SessionOptions { - sessionId: string; - participantId: string; - metadata: string; -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/SignalOptions.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/SignalOptions.ts deleted file mode 100644 index 19737379..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/SignalOptions.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Connection } from '../../../OpenVidu/Connection'; - -export interface SignalOptions { - type?: string; - to?: Connection[]; - data?: string; -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/StreamOptionsServer.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/StreamOptionsServer.ts deleted file mode 100644 index f88c4954..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/StreamOptionsServer.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Filter } from '../../../OpenVidu/Filter'; -import { TypeOfVideo } from '../../Enums/TypeOfVideo'; - -export interface StreamOptionsServer { - id: string; - createdAt: number; - hasAudio: boolean; - hasVideo: boolean; - audioActive: boolean; - videoActive: boolean; - typeOfVideo: TypeOfVideo; - frameRate: number; - videoDimensions: string; - filter: Filter; -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/Capabilities.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/Capabilities.ts deleted file mode 100644 index aa91e337..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/Capabilities.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/** - * See {@link Session.capabilities} - */ -export interface Capabilities { - /** - * `true` if the client can call {@link Session.forceDisconnect}, `false` if not - */ - forceDisconnect: boolean; - - /** - * `true` if the client can call {@link Session.forceUnpublish}, `false` if not - */ - forceUnpublish: boolean; - - /** - * `true` if the client can call {@link Session.publish}, `false` if not - */ - publish: boolean; - - /** - * `true` if the client can call {@link Session.subscribe}, `false` if not (true for every user for now) - */ - subscribe: boolean; -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/Device.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/Device.ts deleted file mode 100644 index 4435d745..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/Device.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/** - * See {@link OpenVidu.getDevices} - */ -export interface Device { - /** - * The kind of device - */ - kind: 'videoinput' | 'audioinput'; - - /** - * Unique ID for the device. Use it on `audioSource` or `videoSource` properties of {@link PublisherProperties} - */ - deviceId: string; - - /** - * Description of the device. An empty string if the user hasn't granted permissions to the site to access the device - */ - label: string; -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration.ts deleted file mode 100644 index 055bc23e..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/** - * See {@link OpenVidu.setAdvancedConfiguration} - */ -export interface OpenViduAdvancedConfiguration { - /** - * Array of [RTCIceServer](https://developer.mozilla.org/en-US/docs/Web/API/RTCIceServer) to be used by OpenVidu Browser. By default OpenVidu will generate the required credentials to use the COTURN server hosted along OpenVidu Server. - * You can also set this property to string 'freeice' to force the use of free STUN servers instead (got thanks to [freeice](https://github.com/DamonOehlman/freeice) library). - * - * > **WARNING**: this value has priority over the standard `iceServers` property of {@link rtcConfiguration}. It will override any value in `OpenViduAdvancedConfiguration.rtcConfiguration.iceServers` - */ - iceServers?: RTCIceServer[] | string; - - /** - * Custom configuration for all [RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection#parameters) objects. This object will be passed as is to all RTCPeerConnection constructor (all Publishers and Subscribers). - */ - rtcConfiguration?: RTCConfiguration; - - /** - * URL to a custom screen share extension for Chrome (always based on ours: [openvidu-screen-sharing-chrome-extension](https://github.com/OpenVidu/openvidu-screen-sharing-chrome-extension)) to be used instead of the default one. - * Must be something like this: `https://chrome.google.com/webstore/detail/YOUR_WEBSTORE_EXTENSION_NAME/YOUR_EXTENSION_ID` - */ - screenShareChromeExtension?: string; - - /** - * Custom configuration for the {@link PublisherSpeakingEvent} feature and the [StreamManagerEvent.streamAudioVolumeChange](/en/stable/api/openvidu-browser/classes/StreamManagerEvent.html) feature. It is an object which includes the following optional properties: - * - `interval`: (number) how frequently the analyser polls the audio stream to check if speaking has started/stopped or audio volume has changed. Default **100** (ms) - * - `threshold`: (number) the volume at which _publisherStartSpeaking_ and _publisherStopSpeaking_ events will be fired. Default **-50** (dB) - * - * This sets the global default configuration that will affect all streams, but you can later customize these values for each specific stream by calling {@link StreamManager.updatePublisherSpeakingEventsOptions} - */ - publisherSpeakingEventsOptions?: { - interval?: number; - threshold?: number; - }; - - /** - * Determines the automatic reconnection process policy. Whenever the client's network drops, OpenVidu Browser starts a reconnection process with OpenVidu Server. After network is recovered, OpenVidu Browser automatically - * inspects all of its media streams to see their status. For any of them that are broken, it asks OpenVidu Server for a forced and silent reconnection. - * - * This policy is technically enough to recover any broken media connection after a network drop, but in practice it has been proven that OpenVidu Browser may think a media connection has properly recovered when in fact it has not. - * This is not a common case, but it may occur. This property allows **forcing OpenVidu Browser to reconnect all of its outgoing and incoming media streams** after a network drop regardless of their supposed status. - * - * Default to `false`. - */ - forceMediaReconnectionAfterNetworkDrop?: boolean; - - /** - * The milliseconds that must elapse after triggering {@link ExceptionEvent} of name [`ICE_CONNECTION_DISCONNECTED`](/en/stable/api/openvidu-browser/enums/ExceptionEventName.html#ICE_CONNECTION_DISCONNECTED) to perform an automatic reconnection process of the affected media stream. - * This automatic reconnection process can only take place if the client still has network connection to OpenVidu Server. If the ICE connection has broken because of a total network drop, - * then no reconnection process will be possible at all. - * - * Default to `4000`. - */ - iceConnectionDisconnectedExceptionTimeout?: number; - - /** - * The milliseconds that must elapse for the {@link ExceptionEvent} of name [`NO_STREAM_PLAYING_EVENT`](/en/stable/api/openvidu-browser/enums/ExceptionEventName.html#NO_STREAM_PLAYING_EVENT) to be fired. - * - * Default to `4000`. - */ - noStreamPlayingEventExceptionTimeout?: number; -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/PublisherProperties.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/PublisherProperties.ts deleted file mode 100644 index e440560c..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/PublisherProperties.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Filter } from '../../../OpenVidu/Filter'; -import { VideoInsertMode } from '../../Enums/VideoInsertMode'; - -/** - * See {@link OpenVidu.initPublisher} - */ -export interface PublisherProperties { - /** - * Which device should provide the audio source. Can be: - * - Property `deviceId` of a {@link Device} - * - `"screen"` to share the screen audio when {@link videoSource} is set to `"screen"`. If {@link videoSource} is not set to `"screen"` this will result in no audio source and a video-only publisher. - * - A MediaStreamTrack obtained from a MediaStream object with {@link OpenVidu.getUserMedia} - * - `false` or null to have a video-only publisher - * @default _Default microphone_ - */ - audioSource?: string | MediaStreamTrack | boolean; - - /** - * Desired framerate of the video in frames per second. - * Limiting the framerate has always effect on browsers Chrome and Opera. Firefox requires that the input device explicitly supports the desired framerate. - * @default undefined - */ - frameRate?: number; - - /** - * How the video element of the publisher should be inserted in the DOM - * @default VideoInsertMode.APPEND - */ - insertMode?: VideoInsertMode | string; - - /** - * Whether the publisher's video will be mirrored in the page or not. Only affects the local view of the publisher in the browser (remote streams will not be mirrored). If `videoSource` is set to "screen" this property is fixed to `false` - * @default true - */ - mirror?: boolean; - - /** - * Whether to initially publish to the session with the audio unmuted or muted. Only makes sense if property `audioSource` is NOT set to *false* or *null*. You can change the audio state later during the session with {@link Publisher.publishAudio} - * @default true - */ - publishAudio?: boolean; - - /** - * Whether to initially publish to the session with the video enabled or disabled. Only makes sense if property `videoSource` is NOT set to *false* or *null*. You can change the video state later during the session with {@link Publisher.publishVideo} - * @default true - */ - publishVideo?: boolean; - - /** - * Resolution of the video: `"320x240"`, `"640x480"`, `"1280x720"` (low, medium and high quality respectively) - * @default "640x480" - */ - resolution?: string; - - /** - * Which device should provide the video source. Can be: - * - Property `deviceId` of a {@link Device} - * - `"screen"` to screen-share. We provide a default screen-shraring extension for Chrome that can run in any domain, but you can customize it so it has your own icon, your own name, etc. Visit this - * [GitHub repository](https://github.com/OpenVidu/openvidu-screen-sharing-chrome-extension/) to learn how. Once you have uploaded your own extension to Chrome Web Store, - * simply call `OpenVidu.setAdvancedConfiguration({screenShareChromeExtension : "https://chrome.google.com/webstore/detail/YOUR_EXTENSION_NAME/YOUR_EXTENSION_ID"})` before calling `OpenVidu.initPublisher(targetElement, {videoSource: "screen"})`. - * For Firefox (<66) `"screen"` string will ask for permissions to share the entire screen. To ask for a specific window or application, use `"window"` string instead (this only applies to Firefox). - * - A MediaStreamTrack obtained from a MediaStream object with {@link OpenVidu.getUserMedia} - * - `false` or null to have an audio-only publisher - * @default _Default camera_ - */ - videoSource?: string | MediaStreamTrack | boolean; - - /** - * Use Simulcast video on WebRTC Publishers. - * Senders will encode duplicate video streams with different qualities, - * so the media server is able to select the most appropriate quality stream - * for each Subscriber. - * This setting is honored only if OpenVidu Server was configured to use the - * mediasoup media server. Otherwise, Simulcast will be disabled. - */ - videoSimulcast?: boolean; - - /** - * **WARNING**: experimental option. This property may change in the near future - * - * Define a filter to apply in the Publisher's stream - */ - filter?: Filter; -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/SignalOptions.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/SignalOptions.ts deleted file mode 100644 index eb9169ed..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/SignalOptions.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Connection } from '../../../OpenVidu/Connection'; - -/** - * See {@link Session.signal} - */ -export interface SignalOptions { - /** - * The actual message of the signal. - */ - data?: string; - - /** - * The participants to whom to send the signal. They will only receive it if they are subscribed to - * event `Session.on('signal')`. If empty or undefined, the signal will be send to all participants. - */ - to?: Connection[]; - - /** - * The type of the signal. Participants subscribed to event `Session.on('signal:type')` will - * receive it. Participants subscribed to `Session.on('signal')` will receive all signals. - */ - type?: string; -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/StreamManagerVideo.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/StreamManagerVideo.ts deleted file mode 100644 index 57898f93..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/StreamManagerVideo.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { VideoInsertMode } from '../../Enums/VideoInsertMode'; - -export interface StreamManagerVideo { - /** - * DOM video element displaying the StreamManager's stream - */ - video: HTMLVideoElement; - - /** - * `id` attribute of the DOM video element displaying the StreamManager's stream - */ - id: string; - - /** - * The DOM HTMLElement assigned as target element when creating a video for the StreamManager. This property is defined when: - * - {@link OpenVidu.initPublisher} or {@link Session.subscribe} methods have been called passing a valid `targetElement` parameter. - * - {@link StreamManager.createVideoElement} has been called. - * - * This property is undefined when: - * - {@link OpenVidu.initPublisher} or {@link Session.subscribe} methods have been called passing *null* or *undefined* as `targetElement` parameter. - * - {@link StreamManager.addVideoElement} has been called. - */ - targetElement?: HTMLElement; - - /** - * How the DOM video element should be inserted with respect to `targetElement`. This property is defined when: - * - {@link OpenVidu.initPublisher} or {@link Session.subscribe} methods have been called passing a valid `targetElement` parameter. - * - {@link StreamManager.createVideoElement} has been called. - * - * This property is undefined when: - * - {@link OpenVidu.initPublisher} or {@link Session.subscribe} methods have been called passing *null* or *undefined* as `targetElement` parameter. - * - {@link StreamManager.addVideoElement} has been called. - */ - insertMode?: VideoInsertMode; - - /** - * @hidden - */ - canplayListenerAdded: boolean; -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/SubscriberProperties.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/SubscriberProperties.ts deleted file mode 100644 index 85779bf8..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/SubscriberProperties.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { VideoInsertMode } from '../../Enums/VideoInsertMode'; - -/** - * See {@link Session.subscribe} - */ -export interface SubscriberProperties { - /** - * How the video element of the subscriber should be inserted in the DOM - * @default VideoInsertMode.APPEND - */ - insertMode?: VideoInsertMode | string; - - /** - * Whether to initially subscribe to the audio track of the stream or not. You can change the audio state later with {@link Subscriber.subscribeToAudio} - * @default true - */ - subscribeToAudio?: boolean; - - /** - * Whether to initially subscribe to the video track of the stream or not. You can change the video state later with {@link Subscriber.subscribeToVideo} - * @default true - */ - subscribeToVideo?: boolean; -} diff --git a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/Mapper.js b/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/Mapper.js deleted file mode 100644 index 83c515d3..00000000 --- a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/Mapper.js +++ /dev/null @@ -1,52 +0,0 @@ -function Mapper() { - var sources = {}; - - this.forEach = function (callback) { - for (var key in sources) { - var source = sources[key]; - - for (var key2 in source) callback(source[key2]); - } - }; - - this.get = function (id, source) { - var ids = sources[source]; - if (ids == undefined) return undefined; - - return ids[id]; - }; - - this.remove = function (id, source) { - var ids = sources[source]; - if (ids == undefined) return; - - delete ids[id]; - - // Check it's empty - for (var i in ids) { - return false; - } - - delete sources[source]; - }; - - this.set = function (value, id, source) { - if (value == undefined) return this.remove(id, source); - - var ids = sources[source]; - if (ids == undefined) sources[source] = ids = {}; - - ids[id] = value; - }; -} - -Mapper.prototype.pop = function (id, source) { - var value = this.get(id, source); - if (value == undefined) return undefined; - - this.remove(id, source); - - return value; -}; - -module.exports = Mapper; diff --git a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/index.js b/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/index.js deleted file mode 100644 index 46b9e3ab..00000000 --- a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * (C) Copyright 2014 Kurento (http://kurento.org/) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -var JsonRpcClient = require('./jsonrpcclient'); - -exports.JsonRpcClient = JsonRpcClient; diff --git a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/jsonrpcclient.js b/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/jsonrpcclient.js deleted file mode 100644 index ace0dd83..00000000 --- a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/jsonrpcclient.js +++ /dev/null @@ -1,280 +0,0 @@ -/* - * (C) Copyright 2014 Kurento (http://kurento.org/) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -var RpcBuilder = require('../'); -var WebSocketWithReconnection = require('./transports/webSocketWithReconnection'); -var OpenViduLogger = require('../../../Logger/OpenViduLogger').OpenViduLogger; - -Date.now = - Date.now || - function () { - return +new Date(); - }; - -var PING_INTERVAL = 5000; - -var RECONNECTING = 'RECONNECTING'; -var CONNECTED = 'CONNECTED'; -var DISCONNECTED = 'DISCONNECTED'; - -var Logger = OpenViduLogger.getInstance(); - -/** - * - * heartbeat: interval in ms for each heartbeat message, - *
- * ws : {
- * 	uri : URI to conntect to,
- * 	onconnected : callback method to invoke when connection is successful,
- * 	ondisconnect : callback method to invoke when the connection is lost (max retries for reconnecting reached),
- * 	onreconnecting : callback method to invoke when the client is reconnecting,
- * 	onreconnected : callback method to invoke when the client successfully reconnects,
- * 	onerror : callback method to invoke when there is an error
- * },
- * rpc : {
- * 	requestTimeout : timeout for a request,
- * 	sessionStatusChanged: callback method for changes in session status,
- * 	mediaRenegotiation: mediaRenegotiation
- * }
- * 
- */ -function JsonRpcClient(configuration) { - var self = this; - - var wsConfig = configuration.ws; - - var notReconnectIfNumLessThan = -1; - - var pingNextNum = 0; - var enabledPings = true; - var pingPongStarted = false; - var pingInterval; - - var status = DISCONNECTED; - - var onreconnecting = wsConfig.onreconnecting; - var onreconnected = wsConfig.onreconnected; - var onconnected = wsConfig.onconnected; - var onerror = wsConfig.onerror; - - configuration.rpc.pull = function (params, request) { - request.reply(null, 'push'); - }; - - wsConfig.onreconnecting = function () { - Logger.debug('--------- ONRECONNECTING -----------'); - if (status === RECONNECTING) { - Logger.error('Websocket already in RECONNECTING state when receiving a new ONRECONNECTING message. Ignoring it'); - return; - } - - stopPing(); - - status = RECONNECTING; - if (onreconnecting) { - onreconnecting(); - } - }; - - wsConfig.onreconnected = function () { - Logger.debug('--------- ONRECONNECTED -----------'); - if (status === CONNECTED) { - Logger.error('Websocket already in CONNECTED state when receiving a new ONRECONNECTED message. Ignoring it'); - return; - } - status = CONNECTED; - - updateNotReconnectIfLessThan(); - - if (onreconnected) { - onreconnected(); - } - }; - - wsConfig.onconnected = function () { - Logger.debug('--------- ONCONNECTED -----------'); - if (status === CONNECTED) { - Logger.error('Websocket already in CONNECTED state when receiving a new ONCONNECTED message. Ignoring it'); - return; - } - status = CONNECTED; - - enabledPings = true; - usePing(); - - if (onconnected) { - onconnected(); - } - }; - - wsConfig.onerror = function (error) { - Logger.debug('--------- ONERROR -----------'); - - status = DISCONNECTED; - - stopPing(); - - if (onerror) { - onerror(error); - } - }; - - var ws = new WebSocketWithReconnection(wsConfig); - - Logger.debug('Connecting websocket to URI: ' + wsConfig.uri); - - var rpcBuilderOptions = { - request_timeout: configuration.rpc.requestTimeout, - ping_request_timeout: configuration.rpc.heartbeatRequestTimeout - }; - - var rpc = new RpcBuilder(RpcBuilder.packers.JsonRPC, rpcBuilderOptions, ws, function (request) { - Logger.debug('Received request: ' + JSON.stringify(request)); - - try { - var func = configuration.rpc[request.method]; - - if (func === undefined) { - Logger.error('Method ' + request.method + ' not registered in client'); - } else { - func(request.params, request); - } - } catch (err) { - Logger.error('Exception processing request: ' + JSON.stringify(request)); - Logger.error(err); - } - }); - - this.send = function (method, params, callback) { - var requestTime = Date.now(); - - rpc.encode(method, params, function (error, result) { - if (error) { - try { - Logger.error( - 'ERROR:' + - error.message + - ' in Request: method:' + - method + - ' params:' + - JSON.stringify(params) + - ' request:' + - error.request - ); - if (error.data) { - Logger.error('ERROR DATA:' + JSON.stringify(error.data)); - } - } catch (e) {} - error.requestTime = requestTime; - } - if (callback) { - if (result != undefined && result.value !== 'pong') { - Logger.debug('Response: ' + JSON.stringify(result)); - } - callback(error, result); - } - }); - }; - - function updateNotReconnectIfLessThan() { - Logger.debug('notReconnectIfNumLessThan = ' + pingNextNum + ' (old=' + notReconnectIfNumLessThan + ')'); - notReconnectIfNumLessThan = pingNextNum; - } - - function sendPing() { - if (enabledPings) { - var params = null; - if (pingNextNum == 0 || pingNextNum == notReconnectIfNumLessThan) { - params = { - interval: configuration.heartbeat || PING_INTERVAL - }; - } - pingNextNum++; - - self.send( - 'ping', - params, - (function (pingNum) { - return function (error, result) { - if (error) { - Logger.debug('Error in ping request #' + pingNum + ' (' + error.message + ')'); - if (pingNum > notReconnectIfNumLessThan) { - enabledPings = false; - updateNotReconnectIfLessThan(); - Logger.debug('Server did not respond to ping message #' + pingNum + '. Reconnecting... '); - ws.reconnectWs(); - } - } - }; - })(pingNextNum) - ); - } else { - Logger.debug('Trying to send ping, but ping is not enabled'); - } - } - - /* - * If configuration.hearbeat has any value, the ping-pong will work with the interval - * of configuration.hearbeat - */ - function usePing() { - if (!pingPongStarted) { - Logger.debug('Starting ping (if configured)'); - pingPongStarted = true; - - if (configuration.heartbeat != undefined) { - pingInterval = setInterval(sendPing, configuration.heartbeat); - sendPing(); - } - } - } - - function stopPing() { - clearInterval(pingInterval); - pingPongStarted = false; - enabledPings = false; - pingNextNum = -1; - rpc.cancel(); - } - - this.close = function (code, reason) { - Logger.debug('Closing with code: ' + code + ' because: ' + reason); - if (pingInterval != undefined) { - Logger.debug('Clearing ping interval'); - clearInterval(pingInterval); - } - pingPongStarted = false; - enabledPings = false; - ws.close(code, reason); - }; - - this.reconnect = function () { - ws.reconnectWs(); - }; - - this.resetPing = function () { - enabledPings = true; - pingNextNum = 0; - usePing(); - }; - - this.getReadyState = function () { - return ws.getReadyState(); - }; -} - -module.exports = JsonRpcClient; diff --git a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/transports/index.js b/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/transports/index.js deleted file mode 100644 index 59c85285..00000000 --- a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/transports/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * (C) Copyright 2014 Kurento (http://kurento.org/) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -var WebSocketWithReconnection = require('./webSocketWithReconnection'); - -exports.WebSocketWithReconnection = WebSocketWithReconnection; diff --git a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/transports/webSocketWithReconnection.js b/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/transports/webSocketWithReconnection.js deleted file mode 100644 index 6888dca8..00000000 --- a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/transports/webSocketWithReconnection.js +++ /dev/null @@ -1,161 +0,0 @@ -/* - * (C) Copyright 2013-2015 Kurento (http://kurento.org/) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -var OpenViduLogger = require('../../../../Logger/OpenViduLogger').OpenViduLogger; -var Logger = OpenViduLogger.getInstance(); - -var MAX_RETRIES = 2000; // Forever... -var RETRY_TIME_MS = 3000; // FIXME: Implement exponential wait times... - -var CONNECTING = 0; -var OPEN = 1; -var CLOSING = 2; -var CLOSED = 3; - -/* -config = { -uri : wsUri, -onconnected : callback method to invoke when connection is successful, -ondisconnect : callback method to invoke when the connection is lost (max retries for reconnecting reached), -onreconnecting : callback method to invoke when the client is reconnecting, -onreconnected : callback method to invoke when the client successfully reconnects, -}; -*/ -function WebSocketWithReconnection(config) { - var closing = false; - var registerMessageHandler; - var wsUri = config.uri; - var reconnecting = false; - - var ws = new WebSocket(wsUri); - - ws.onopen = () => { - Logger.debug('WebSocket connected to ' + wsUri); - if (config.onconnected) { - config.onconnected(); - } - }; - - ws.onerror = (error) => { - Logger.error('Could not connect to ' + wsUri + ' (invoking onerror if defined)', error); - if (config.onerror) { - config.onerror(error); - } - }; - - var reconnectionOnClose = () => { - if (ws.readyState === CLOSED) { - if (closing) { - Logger.debug('Connection closed by user'); - } else { - if (config.ismasternodecrashed()) { - Logger.error('Master Node has crashed. Stopping reconnection process'); - } else { - Logger.debug('Connection closed unexpectedly. Reconnecting...'); - reconnect(MAX_RETRIES, 1); - } - } - } else { - Logger.debug('Close callback from previous websocket. Ignoring it'); - } - }; - - ws.onclose = reconnectionOnClose; - - function reconnect(maxRetries, numRetries) { - Logger.debug('reconnect (attempt #' + numRetries + ', max=' + maxRetries + ')'); - if (numRetries === 1) { - if (reconnecting) { - Logger.warn('Trying to reconnect when already reconnecting... Ignoring this reconnection.'); - return; - } else { - reconnecting = true; - } - if (config.onreconnecting) { - config.onreconnecting(); - } - } - reconnectAux(maxRetries, numRetries); - } - - function addReconnectionQueryParamsIfMissing(uriString) { - var searchParams = new URLSearchParams(new URL(uriString).search); - if (!searchParams.has('reconnect')) { - uriString = Array.from(searchParams).length > 0 ? uriString + '&reconnect=true' : uriString + '?reconnect=true'; - } - return uriString; - } - - function reconnectAux(maxRetries, numRetries) { - Logger.debug('Reconnection attempt #' + numRetries); - ws.close(4104, 'Connection closed for reconnection'); - - wsUri = addReconnectionQueryParamsIfMissing(wsUri); - ws = new WebSocket(wsUri); - - ws.onopen = () => { - Logger.debug('Reconnected to ' + wsUri + ' after ' + numRetries + ' attempts...'); - reconnecting = false; - registerMessageHandler(); - if (config.onreconnected) { - config.onreconnected(); - } - ws.onclose = reconnectionOnClose; - }; - - ws.onerror = (error) => { - Logger.warn('Reconnection error: ', error); - if (numRetries === maxRetries) { - if (config.ondisconnect) { - config.ondisconnect(); - } - } else { - setTimeout(() => { - reconnect(maxRetries, numRetries + 1); - }, RETRY_TIME_MS); - } - }; - } - - this.close = (code, reason) => { - closing = true; - ws.close(code, reason); - }; - - this.reconnectWs = () => { - Logger.debug('reconnectWs'); - reconnect(MAX_RETRIES, 1); - }; - - this.send = (message) => { - ws.send(message); - }; - - this.addEventListener = (type, callback) => { - registerMessageHandler = () => { - ws.addEventListener(type, callback); - }; - registerMessageHandler(); - }; - - this.getReadyState = () => { - return ws.readyState; - }; -} - -module.exports = WebSocketWithReconnection; diff --git a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/index.js b/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/index.js deleted file mode 100644 index fb448400..00000000 --- a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/index.js +++ /dev/null @@ -1,687 +0,0 @@ -/* - * (C) Copyright 2014 Kurento (http://kurento.org/) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -var defineProperty_IE8 = false; -if (Object.defineProperty) { - try { - Object.defineProperty({}, 'x', {}); - } catch (e) { - defineProperty_IE8 = true; - } -} - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind -if (!Function.prototype.bind) { - Function.prototype.bind = function (oThis) { - if (typeof this !== 'function') { - // closest thing possible to the ECMAScript 5 - // internal IsCallable function - throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); - } - - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function () {}, - fBound = function () { - return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); - }; - - fNOP.prototype = this.prototype; - fBound.prototype = new fNOP(); - - return fBound; - }; -} - -var EventEmitter = require('events').EventEmitter; - -var inherits = require('inherits'); - -var packers = require('./packers'); -var Mapper = require('./Mapper'); - -var BASE_TIMEOUT = 5000; - -function unifyResponseMethods(responseMethods) { - if (!responseMethods) return {}; - - for (var key in responseMethods) { - var value = responseMethods[key]; - - if (typeof value == 'string') - responseMethods[key] = { - response: value - }; - } - - return responseMethods; -} - -function unifyTransport(transport) { - if (!transport) return; - - // Transport as a function - if (transport instanceof Function) - return { - send: transport - }; - - // WebSocket & DataChannel - if (transport.send instanceof Function) return transport; - - // Message API (Inter-window & WebWorker) - if (transport.postMessage instanceof Function) { - transport.send = transport.postMessage; - return transport; - } - - // Stream API - if (transport.write instanceof Function) { - transport.send = transport.write; - return transport; - } - - // Transports that only can receive messages, but not send - if (transport.onmessage !== undefined) return; - if (transport.pause instanceof Function) return; - - throw new SyntaxError('Transport is not a function nor a valid object'); -} - -/** - * Representation of a RPC notification - * - * @class - * - * @constructor - * - * @param {String} method -method of the notification - * @param params - parameters of the notification - */ -function RpcNotification(method, params) { - if (defineProperty_IE8) { - this.method = method; - this.params = params; - } else { - Object.defineProperty(this, 'method', { - value: method, - enumerable: true - }); - Object.defineProperty(this, 'params', { - value: params, - enumerable: true - }); - } -} - -/** - * @class - * - * @constructor - * - * @param {object} packer - * - * @param {object} [options] - * - * @param {object} [transport] - * - * @param {Function} [onRequest] - */ -function RpcBuilder(packer, options, transport, onRequest) { - var self = this; - - if (!packer) throw new SyntaxError('Packer is not defined'); - - if (!packer.pack || !packer.unpack) throw new SyntaxError('Packer is invalid'); - - var responseMethods = unifyResponseMethods(packer.responseMethods); - - if (options instanceof Function) { - if (transport != undefined) throw new SyntaxError("There can't be parameters after onRequest"); - - onRequest = options; - transport = undefined; - options = undefined; - } - - if (options && options.send instanceof Function) { - if (transport && !(transport instanceof Function)) throw new SyntaxError('Only a function can be after transport'); - - onRequest = transport; - transport = options; - options = undefined; - } - - if (transport instanceof Function) { - if (onRequest != undefined) throw new SyntaxError("There can't be parameters after onRequest"); - - onRequest = transport; - transport = undefined; - } - - if (transport && transport.send instanceof Function) - if (onRequest && !(onRequest instanceof Function)) throw new SyntaxError('Only a function can be after transport'); - - options = options || {}; - - EventEmitter.call(this); - - if (onRequest) this.on('request', onRequest); - - if (defineProperty_IE8) this.peerID = options.peerID; - else - Object.defineProperty(this, 'peerID', { - value: options.peerID - }); - - var max_retries = options.max_retries || 0; - - function transportMessage(event) { - self.decode(event.data || event); - } - - this.getTransport = function () { - return transport; - }; - this.setTransport = function (value) { - // Remove listener from old transport - if (transport) { - // W3C transports - if (transport.removeEventListener) transport.removeEventListener('message', transportMessage); - // Node.js Streams API - else if (transport.removeListener) transport.removeListener('data', transportMessage); - } - - // Set listener on new transport - if (value) { - // W3C transports - if (value.addEventListener) value.addEventListener('message', transportMessage); - // Node.js Streams API - else if (value.addListener) value.addListener('data', transportMessage); - } - - transport = unifyTransport(value); - }; - - if (!defineProperty_IE8) - Object.defineProperty(this, 'transport', { - get: this.getTransport.bind(this), - set: this.setTransport.bind(this) - }); - - this.setTransport(transport); - - var request_timeout = options.request_timeout || BASE_TIMEOUT; - var ping_request_timeout = options.ping_request_timeout || request_timeout; - var response_timeout = options.response_timeout || BASE_TIMEOUT; - var duplicates_timeout = options.duplicates_timeout || BASE_TIMEOUT; - - var requestID = 0; - - var requests = new Mapper(); - var responses = new Mapper(); - var processedResponses = new Mapper(); - - var message2Key = {}; - - /** - * Store the response to prevent to process duplicate request later - */ - function storeResponse(message, id, dest) { - var response = { - message: message, - /** Timeout to auto-clean old responses */ - timeout: setTimeout(function () { - responses.remove(id, dest); - }, response_timeout) - }; - - responses.set(response, id, dest); - } - - /** - * Store the response to ignore duplicated messages later - */ - function storeProcessedResponse(ack, from) { - var timeout = setTimeout(function () { - processedResponses.remove(ack, from); - }, duplicates_timeout); - - processedResponses.set(timeout, ack, from); - } - - /** - * Representation of a RPC request - * - * @class - * @extends RpcNotification - * - * @constructor - * - * @param {String} method -method of the notification - * @param params - parameters of the notification - * @param {Integer} id - identifier of the request - * @param [from] - source of the notification - */ - function RpcRequest(method, params, id, from, transport) { - RpcNotification.call(this, method, params); - - this.getTransport = function () { - return transport; - }; - this.setTransport = function (value) { - transport = unifyTransport(value); - }; - - if (!defineProperty_IE8) - Object.defineProperty(this, 'transport', { - get: this.getTransport.bind(this), - set: this.setTransport.bind(this) - }); - - var response = responses.get(id, from); - - /** - * @constant {Boolean} duplicated - */ - if (!(transport || self.getTransport())) { - if (defineProperty_IE8) this.duplicated = Boolean(response); - else - Object.defineProperty(this, 'duplicated', { - value: Boolean(response) - }); - } - - var responseMethod = responseMethods[method]; - - this.pack = packer.pack.bind(packer, this, id); - - /** - * Generate a response to this request - * - * @param {Error} [error] - * @param {*} [result] - * - * @returns {string} - */ - this.reply = function (error, result, transport) { - // Fix optional parameters - if (error instanceof Function || (error && error.send instanceof Function)) { - if (result != undefined) throw new SyntaxError("There can't be parameters after callback"); - - transport = error; - result = null; - error = undefined; - } else if (result instanceof Function || (result && result.send instanceof Function)) { - if (transport != undefined) throw new SyntaxError("There can't be parameters after callback"); - - transport = result; - result = null; - } - - transport = unifyTransport(transport); - - // Duplicated request, remove old response timeout - if (response) clearTimeout(response.timeout); - - if (from != undefined) { - if (error) error.dest = from; - - if (result) result.dest = from; - } - - var message; - - // New request or overriden one, create new response with provided data - if (error || result != undefined) { - if (self.peerID != undefined) { - if (error) error.from = self.peerID; - else result.from = self.peerID; - } - - // Protocol indicates that responses has own request methods - if (responseMethod) { - if (responseMethod.error == undefined && error) - message = { - error: error - }; - else { - var method = error ? responseMethod.error : responseMethod.response; - - message = { - method: method, - params: error || result - }; - } - } else - message = { - error: error, - result: result - }; - - message = packer.pack(message, id); - } - - // Duplicate & not-overriden request, re-send old response - else if (response) message = response.message; - // New empty reply, response null value - else - message = packer.pack( - { - result: null - }, - id - ); - - // Store the response to prevent to process a duplicated request later - storeResponse(message, id, from); - - // Return the stored response so it can be directly send back - transport = transport || this.getTransport() || self.getTransport(); - - if (transport) return transport.send(message); - - return message; - }; - } - inherits(RpcRequest, RpcNotification); - - function cancel(message) { - var key = message2Key[message]; - if (!key) return; - - delete message2Key[message]; - - var request = requests.pop(key.id, key.dest); - if (!request) return; - - clearTimeout(request.timeout); - - // Start duplicated responses timeout - storeProcessedResponse(key.id, key.dest); - } - - /** - * Allow to cancel a request and don't wait for a response - * - * If `message` is not given, cancel all the request - */ - this.cancel = function (message) { - if (message) return cancel(message); - - for (var message in message2Key) cancel(message); - }; - - this.close = function () { - // Prevent to receive new messages - var transport = this.getTransport(); - if (transport && transport.close) transport.close(4003, 'Cancel request'); - - // Request & processed responses - this.cancel(); - - processedResponses.forEach(clearTimeout); - - // Responses - responses.forEach(function (response) { - clearTimeout(response.timeout); - }); - }; - - /** - * Generates and encode a JsonRPC 2.0 message - * - * @param {String} method -method of the notification - * @param params - parameters of the notification - * @param [dest] - destination of the notification - * @param {object} [transport] - transport where to send the message - * @param [callback] - function called when a response to this request is - * received. If not defined, a notification will be send instead - * - * @returns {string} A raw JsonRPC 2.0 request or notification string - */ - this.encode = function (method, params, dest, transport, callback) { - // Fix optional parameters - if (params instanceof Function) { - if (dest != undefined) throw new SyntaxError("There can't be parameters after callback"); - - callback = params; - transport = undefined; - dest = undefined; - params = undefined; - } else if (dest instanceof Function) { - if (transport != undefined) throw new SyntaxError("There can't be parameters after callback"); - - callback = dest; - transport = undefined; - dest = undefined; - } else if (transport instanceof Function) { - if (callback != undefined) throw new SyntaxError("There can't be parameters after callback"); - - callback = transport; - transport = undefined; - } - - if (self.peerID != undefined) { - params = params || {}; - - params.from = self.peerID; - } - - if (dest != undefined) { - params = params || {}; - - params.dest = dest; - } - - // Encode message - var message = { - method: method, - params: params - }; - - if (callback) { - var id = requestID++; - var retried = 0; - - message = packer.pack(message, id); - - function dispatchCallback(error, result) { - self.cancel(message); - - callback(error, result); - } - - var request = { - message: message, - callback: dispatchCallback, - responseMethods: responseMethods[method] || {} - }; - - var encode_transport = unifyTransport(transport); - - function sendRequest(transport) { - var rt = method === 'ping' ? ping_request_timeout : request_timeout; - request.timeout = setTimeout(timeout, rt * Math.pow(2, retried++)); - message2Key[message] = { - id: id, - dest: dest - }; - requests.set(request, id, dest); - - transport = transport || encode_transport || self.getTransport(); - if (transport) return transport.send(message); - - return message; - } - - function retry(transport) { - transport = unifyTransport(transport); - - console.warn(retried + ' retry for request message:', message); - - var timeout = processedResponses.pop(id, dest); - clearTimeout(timeout); - - return sendRequest(transport); - } - - function timeout() { - if (retried < max_retries) return retry(transport); - - var error = new Error('Request has timed out'); - error.request = message; - - error.retry = retry; - - dispatchCallback(error); - } - - return sendRequest(transport); - } - - // Return the packed message - message = packer.pack(message); - - transport = transport || this.getTransport(); - if (transport) return transport.send(message); - - return message; - }; - - /** - * Decode and process a JsonRPC 2.0 message - * - * @param {string} message - string with the content of the message - * - * @returns {RpcNotification|RpcRequest|undefined} - the representation of the - * notification or the request. If a response was processed, it will return - * `undefined` to notify that it was processed - * - * @throws {TypeError} - Message is not defined - */ - this.decode = function (message, transport) { - if (!message) throw new TypeError('Message is not defined'); - - try { - message = packer.unpack(message); - } catch (e) { - // Ignore invalid messages - return console.debug(e, message); - } - - var id = message.id; - var ack = message.ack; - var method = message.method; - var params = message.params || {}; - - var from = params.from; - var dest = params.dest; - - // Ignore messages send by us - if (self.peerID != undefined && from == self.peerID) return; - - // Notification - if (id == undefined && ack == undefined) { - var notification = new RpcNotification(method, params); - - if (self.emit('request', notification)) return; - return notification; - } - - function processRequest() { - // If we have a transport and it's a duplicated request, reply inmediatly - transport = unifyTransport(transport) || self.getTransport(); - if (transport) { - var response = responses.get(id, from); - if (response) return transport.send(response.message); - } - - var idAck = id != undefined ? id : ack; - var request = new RpcRequest(method, params, idAck, from, transport); - - if (self.emit('request', request)) return; - return request; - } - - function processResponse(request, error, result) { - request.callback(error, result); - } - - function duplicatedResponse(timeout) { - console.warn('Response already processed', message); - - // Update duplicated responses timeout - clearTimeout(timeout); - storeProcessedResponse(ack, from); - } - - // Request, or response with own method - if (method) { - // Check if it's a response with own method - if (dest == undefined || dest == self.peerID) { - var request = requests.get(ack, from); - if (request) { - var responseMethods = request.responseMethods; - - if (method == responseMethods.error) return processResponse(request, params); - - if (method == responseMethods.response) return processResponse(request, null, params); - - return processRequest(); - } - - var processed = processedResponses.get(ack, from); - if (processed) return duplicatedResponse(processed); - } - - // Request - return processRequest(); - } - - var error = message.error; - var result = message.result; - - // Ignore responses not send to us - if (error && error.dest && error.dest != self.peerID) return; - if (result && result.dest && result.dest != self.peerID) return; - - // Response - var request = requests.get(ack, from); - if (!request) { - var processed = processedResponses.get(ack, from); - if (processed) return duplicatedResponse(processed); - - return console.warn('No callback was defined for this message', message); - } - - // Process response - processResponse(request, error, result); - }; -} -inherits(RpcBuilder, EventEmitter); - -RpcBuilder.RpcNotification = RpcNotification; - -module.exports = RpcBuilder; - -var clients = require('./clients'); -var transports = require('./clients/transports'); - -RpcBuilder.clients = clients; -RpcBuilder.clients.transports = transports; -RpcBuilder.packers = packers; diff --git a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/packers/JsonRPC.js b/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/packers/JsonRPC.js deleted file mode 100644 index b318c6b9..00000000 --- a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/packers/JsonRPC.js +++ /dev/null @@ -1,85 +0,0 @@ -/** - * JsonRPC 2.0 packer - */ - -/** - * Pack a JsonRPC 2.0 message - * - * @param {Object} message - object to be packaged. It requires to have all the - * fields needed by the JsonRPC 2.0 message that it's going to be generated - * - * @return {String} - the stringified JsonRPC 2.0 message - */ -function pack(message, id) { - var result = { - jsonrpc: '2.0' - }; - - // Request - if (message.method) { - result.method = message.method; - - if (message.params) result.params = message.params; - - // Request is a notification - if (id != undefined) result.id = id; - } - - // Response - else if (id != undefined) { - if (message.error) { - if (message.result !== undefined) throw new TypeError('Both result and error are defined'); - - result.error = message.error; - } else if (message.result !== undefined) result.result = message.result; - else throw new TypeError('No result or error is defined'); - - result.id = id; - } - - return JSON.stringify(result); -} - -/** - * Unpack a JsonRPC 2.0 message - * - * @param {String} message - string with the content of the JsonRPC 2.0 message - * - * @throws {TypeError} - Invalid JsonRPC version - * - * @return {Object} - object filled with the JsonRPC 2.0 message content - */ -function unpack(message) { - var result = message; - - if (typeof message === 'string' || message instanceof String) { - result = JSON.parse(message); - } - - // Check if it's a valid message - - var version = result.jsonrpc; - if (version !== '2.0') throw new TypeError("Invalid JsonRPC version '" + version + "': " + message); - - // Response - if (result.method == undefined) { - if (result.id == undefined) throw new TypeError('Invalid message: ' + message); - - var result_defined = result.result !== undefined; - var error_defined = result.error !== undefined; - - // Check only result or error is defined, not both or none - if (result_defined && error_defined) throw new TypeError('Both result and error are defined: ' + message); - - if (!result_defined && !error_defined) throw new TypeError('No result or error is defined: ' + message); - - result.ack = result.id; - delete result.id; - } - - // Return unpacked message - return result; -} - -exports.pack = pack; -exports.unpack = unpack; diff --git a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/packers/XmlRPC.js b/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/packers/XmlRPC.js deleted file mode 100644 index 0a437e68..00000000 --- a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/packers/XmlRPC.js +++ /dev/null @@ -1,10 +0,0 @@ -function pack(message) { - throw new TypeError('Not yet implemented'); -} - -function unpack(message) { - throw new TypeError('Not yet implemented'); -} - -exports.pack = pack; -exports.unpack = unpack; diff --git a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/packers/index.js b/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/packers/index.js deleted file mode 100644 index 34949b95..00000000 --- a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/packers/index.js +++ /dev/null @@ -1,5 +0,0 @@ -var JsonRPC = require('./JsonRPC'); -var XmlRPC = require('./XmlRPC'); - -exports.JsonRPC = JsonRPC; -exports.XmlRPC = XmlRPC; diff --git a/openvidu-browser/src/OpenViduInternal/Logger/ConsoleLogger.ts b/openvidu-browser/src/OpenViduInternal/Logger/ConsoleLogger.ts deleted file mode 100644 index 58584fdd..00000000 --- a/openvidu-browser/src/OpenViduInternal/Logger/ConsoleLogger.ts +++ /dev/null @@ -1,41 +0,0 @@ -type ConsoleFunction = (...data: any) => void; -export class ConsoleLogger { - /** - * @hidden - */ - logger: Console; - - /** - * @hidden - */ - log: ConsoleFunction; - - /** - * @hidden - */ - info: ConsoleFunction; - - /** - * @hidden - */ - debug: ConsoleFunction; - - /** - * @hidden - */ - warn: ConsoleFunction; - - /** - * @hidden - */ - error: ConsoleFunction; - - constructor(console: Console) { - this.logger = console; - (this.log = console.log), - (this.info = console.info), - (this.debug = console.debug), - (this.warn = console.warn), - (this.error = console.error); - } -} diff --git a/openvidu-browser/src/OpenViduInternal/Logger/OpenViduLogger.ts b/openvidu-browser/src/OpenViduInternal/Logger/OpenViduLogger.ts deleted file mode 100644 index deaad660..00000000 --- a/openvidu-browser/src/OpenViduInternal/Logger/OpenViduLogger.ts +++ /dev/null @@ -1,285 +0,0 @@ -import { JL } from 'jsnlog'; -import { OpenVidu } from '../../OpenVidu/OpenVidu'; -import { ConsoleLogger } from './ConsoleLogger'; -import { OpenViduLoggerConfiguration } from './OpenViduLoggerConfiguration'; - -export class OpenViduLogger { - private static instance: OpenViduLogger; - - private JSNLOG_URL: string = '/openvidu/elk/openvidu-browser-logs'; - private MAX_JSNLOG_BATCH_LOG_MESSAGES: number = 100; - private MAX_MSECONDS_BATCH_MESSAGES: number = 5000; - private MAX_LENGTH_STRING_JSON: number = 1000; - - private defaultConsoleLogger: ConsoleLogger = new ConsoleLogger(globalThis.console); - - private currentAppender: any; - - private isProdMode = false; - private isJSNLogSetup = false; - - // This two variables are used to restart JSNLog - // on different sessions and different userIds - private loggingSessionId: string | undefined; - - /** - * @hidden - */ - static configureJSNLog(openVidu: OpenVidu, token: string) { - try { - // If dev mode or... - if ( - globalThis['LOG_JSNLOG_RESULTS'] || - // If instance is created and it is OpenVidu Pro - (this.instance && - openVidu.isAtLeastPro && - // If logs are enabled - this.instance.isOpenViduBrowserLogsDebugActive(openVidu) && - // Only reconfigure it if session or finalUserId has changed - this.instance.canConfigureJSNLog(openVidu, this.instance)) - ) { - // Check if app logs can be sent - // and replace console.log function to send - // logs of the application - if (openVidu.sendBrowserLogs === OpenViduLoggerConfiguration.debug_app) { - this.instance.replaceWindowConsole(); - } - - // isJSNLogSetup will not be true until completed setup - this.instance.isJSNLogSetup = false; - this.instance.info('Configuring JSNLogs.'); - - const finalUserId = openVidu.finalUserId; - const sessionId = openVidu.session.sessionId; - - const beforeSendCallback = (xhr) => { - // If 401 or 403 or 404 modify ready and status so JSNLog don't retry to send logs - // https://github.com/mperdeck/jsnlog.js/blob/v2.30.0/jsnlog.ts#L805-L818 - const parentReadyStateFunction = xhr.onreadystatechange; - xhr.onreadystatechange = () => { - if (this.isInvalidResponse(xhr)) { - Object.defineProperty(xhr, 'readyState', { value: 4 }); - Object.defineProperty(xhr, 'status', { value: 200 }); - // Disable JSNLog too to not send periodically errors - this.instance.disableLogger(); - } - parentReadyStateFunction(); - }; - - // Headers to identify and authenticate logs - xhr.setRequestHeader('Authorization', 'Basic ' + btoa(`${finalUserId}%/%${sessionId}` + ':' + token)); - xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); - // Additional headers for OpenVidu - xhr.setRequestHeader('OV-Final-User-Id', finalUserId); - xhr.setRequestHeader('OV-Session-Id', sessionId); - xhr.setRequestHeader('OV-Token', token); - }; - - // Creation of the appender. - this.instance.currentAppender = JL.createAjaxAppender(`appender-${finalUserId}-${sessionId}`); - this.instance.currentAppender.setOptions({ - beforeSend: beforeSendCallback, - maxBatchSize: 1000, - batchSize: this.instance.MAX_JSNLOG_BATCH_LOG_MESSAGES, - batchTimeout: this.instance.MAX_MSECONDS_BATCH_MESSAGES - }); - - // Avoid circular dependencies - const logSerializer = (obj): string => { - const getCircularReplacer = () => { - const seen = new WeakSet(); - return (key, value) => { - if (typeof value === 'object' && value != null) { - if (seen.has(value) || (globalThis.HTMLElement && value instanceof HTMLElement)) { - return; - } - seen.add(value); - } - return value; - }; - }; - - // Cut long messages - let stringifyJson = JSON.stringify(obj, getCircularReplacer()); - if (stringifyJson.length > this.instance.MAX_LENGTH_STRING_JSON) { - stringifyJson = `${stringifyJson.substring(0, this.instance.MAX_LENGTH_STRING_JSON)}...`; - } - - if (globalThis['LOG_JSNLOG_RESULTS']) { - console.log(stringifyJson); - } - - return stringifyJson; - }; - - // Initialize JL to send logs - JL.setOptions({ - defaultAjaxUrl: openVidu.httpUri + this.instance.JSNLOG_URL, - serialize: logSerializer, - enabled: true - }); - JL().setOptions({ - appenders: [this.instance.currentAppender] - }); - - this.instance.isJSNLogSetup = true; - this.instance.loggingSessionId = sessionId; - this.instance.info('JSNLog configured.'); - } - } catch (e) { - // Print error - console.error('Error configuring JSNLog: '); - console.error(e); - // Restore defaults values just in case any exception happen- - this.instance.disableLogger(); - } - } - - /** - * @hidden - */ - static getInstance(): OpenViduLogger { - if (!OpenViduLogger.instance) { - OpenViduLogger.instance = new OpenViduLogger(); - } - return OpenViduLogger.instance; - } - - private static isInvalidResponse(xhr: XMLHttpRequest) { - return xhr.status == 401 || xhr.status == 403 || xhr.status == 404 || xhr.status == 0; - } - - private canConfigureJSNLog(openVidu: OpenVidu, logger: OpenViduLogger): boolean { - return openVidu.session.sessionId != logger.loggingSessionId; - } - - private isOpenViduBrowserLogsDebugActive(openVidu: OpenVidu) { - return ( - openVidu.sendBrowserLogs === OpenViduLoggerConfiguration.debug || - openVidu.sendBrowserLogs === OpenViduLoggerConfiguration.debug_app - ); - } - - // Return console functions with jsnlog integration - private getConsoleWithJSNLog() { - return (function (openViduLogger: OpenViduLogger) { - return { - log: function (...args) { - openViduLogger.defaultConsoleLogger.log.apply(openViduLogger.defaultConsoleLogger.logger, arguments); - if (openViduLogger.isJSNLogSetup) { - JL().info(arguments); - } - }, - info: function (...args) { - openViduLogger.defaultConsoleLogger.info.apply(openViduLogger.defaultConsoleLogger.logger, arguments); - if (openViduLogger.isJSNLogSetup) { - JL().info(arguments); - } - }, - debug: function (...args) { - openViduLogger.defaultConsoleLogger.debug.apply(openViduLogger.defaultConsoleLogger.logger, arguments); - }, - warn: function (...args) { - openViduLogger.defaultConsoleLogger.warn.apply(openViduLogger.defaultConsoleLogger.logger, arguments); - if (openViduLogger.isJSNLogSetup) { - JL().warn(arguments); - } - }, - error: function (...args) { - openViduLogger.defaultConsoleLogger.error.apply(openViduLogger.defaultConsoleLogger.logger, arguments); - if (openViduLogger.isJSNLogSetup) { - JL().error(arguments); - } - } - }; - })(this); - } - - private replaceWindowConsole() { - globalThis.console = this.defaultConsoleLogger.logger; - globalThis.console.log = this.getConsoleWithJSNLog().log; - globalThis.console.info = this.getConsoleWithJSNLog().info; - globalThis.console.debug = this.getConsoleWithJSNLog().debug; - globalThis.console.warn = this.getConsoleWithJSNLog().warn; - globalThis.console.error = this.getConsoleWithJSNLog().error; - } - - private disableLogger() { - JL.setOptions({ enabled: false }); - this.isJSNLogSetup = false; - this.loggingSessionId = undefined; - this.currentAppender = undefined; - globalThis.console = this.defaultConsoleLogger.logger; - globalThis.console.log = this.defaultConsoleLogger.log; - globalThis.console.info = this.defaultConsoleLogger.info; - globalThis.console.debug = this.defaultConsoleLogger.debug; - globalThis.console.warn = this.defaultConsoleLogger.warn; - globalThis.console.error = this.defaultConsoleLogger.error; - } - - /** - * @hidden - */ - log(...args: any[]) { - if (!this.isProdMode) { - this.defaultConsoleLogger.log.apply(this.defaultConsoleLogger.logger, arguments); - } - if (this.isJSNLogSetup) { - JL().info(arguments); - } - } - - /** - * @hidden - */ - debug(...args: any[]) { - if (!this.isProdMode) { - this.defaultConsoleLogger.debug.apply(this.defaultConsoleLogger.logger, arguments); - } - } - - /** - * @hidden - */ - info(...args: any[]) { - if (!this.isProdMode) { - this.defaultConsoleLogger.info.apply(this.defaultConsoleLogger.logger, arguments); - } - if (this.isJSNLogSetup) { - JL().info(arguments); - } - } - - /** - * @hidden - */ - warn(...args: any[]) { - this.defaultConsoleLogger.warn.apply(this.defaultConsoleLogger.logger, arguments); - if (this.isJSNLogSetup) { - JL().warn(arguments); - } - } - - /** - * @hidden - */ - error(...args: any[]) { - this.defaultConsoleLogger.error.apply(this.defaultConsoleLogger.logger, arguments); - if (this.isJSNLogSetup) { - JL().error(arguments); - } - } - - /** - * @hidden - */ - flush() { - if (this.isJSNLogSetup && this.currentAppender != null) { - this.currentAppender.sendBatch(); - } - } - - enableProdMode() { - this.isProdMode = true; - } -} diff --git a/openvidu-browser/src/OpenViduInternal/Logger/OpenViduLoggerConfiguration.ts b/openvidu-browser/src/OpenViduInternal/Logger/OpenViduLoggerConfiguration.ts deleted file mode 100644 index ff8d34ac..00000000 --- a/openvidu-browser/src/OpenViduInternal/Logger/OpenViduLoggerConfiguration.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum OpenViduLoggerConfiguration { - disabled = 'disabled', - debug = 'debug', - debug_app = 'debug_app' -} diff --git a/openvidu-browser/src/OpenViduInternal/ScreenSharing/Screen-Capturing-Auto.js b/openvidu-browser/src/OpenViduInternal/ScreenSharing/Screen-Capturing-Auto.js deleted file mode 100644 index 250ca60d..00000000 --- a/openvidu-browser/src/OpenViduInternal/ScreenSharing/Screen-Capturing-Auto.js +++ /dev/null @@ -1,233 +0,0 @@ -// Last time updated on June 08, 2018 - -// Latest file can be found here: https://cdn.webrtc-experiment.com/getScreenId.js - -// Muaz Khan - www.MuazKhan.com -// MIT License - www.WebRTC-Experiment.com/licence -// Documentation - https://github.com/muaz-khan/getScreenId. - -// ______________ -// getScreenId.js - -/* -getScreenId(function (error, sourceId, screen_constraints) { - // error == null || 'permission-denied' || 'not-installed' || 'installed-disabled' || 'not-chrome' - // sourceId == null || 'string' || 'firefox' - - if(microsoftEdge) { - navigator.getDisplayMedia(screen_constraints).then(onSuccess, onFailure); - } - else { - navigator.mediaDevices.getUserMedia(screen_constraints).then(onSuccess)catch(onFailure); - } -}, 'pass second parameter only if you want system audio'); -*/ - -globalThis.getScreenId = function (firefoxString, callback, custom_parameter) { - if (navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator.msSaveOrOpenBlob || !!navigator.msSaveBlob)) { - // microsoft edge => navigator.getDisplayMedia(screen_constraints).then(onSuccess, onFailure); - callback({ - video: true - }); - return; - } - - // for Firefox: - // sourceId == 'firefox' - // screen_constraints = {...} - if (!!navigator.mozGetUserMedia) { - callback(null, 'firefox', { - video: { - mozMediaSource: firefoxString, - mediaSource: firefoxString - } - }); - return; - } - - globalThis.addEventListener('message', onIFrameCallback); - - function onIFrameCallback(event) { - if (!event.data) return; - - if (event.data.chromeMediaSourceId) { - if (event.data.chromeMediaSourceId === 'PermissionDeniedError') { - callback('permission-denied'); - } else { - callback( - null, - event.data.chromeMediaSourceId, - getScreenConstraints(null, event.data.chromeMediaSourceId, event.data.canRequestAudioTrack) - ); - } - - // this event listener is no more needed - globalThis.removeEventListener('message', onIFrameCallback); - } - - if (event.data.chromeExtensionStatus) { - callback(event.data.chromeExtensionStatus, null, getScreenConstraints(event.data.chromeExtensionStatus)); - - // this event listener is no more needed - globalThis.removeEventListener('message', onIFrameCallback); - } - } - - if (!custom_parameter) { - setTimeout(postGetSourceIdMessage, 100); - } else { - setTimeout(function () { - postGetSourceIdMessage(custom_parameter); - }, 100); - } -}; - -function getScreenConstraints(error, sourceId, canRequestAudioTrack) { - var screen_constraints = { - audio: false, - video: { - mandatory: { - chromeMediaSource: error ? 'screen' : 'desktop', - maxWidth: globalThis.screen.width > 1920 ? globalThis.screen.width : 1920, - maxHeight: globalThis.screen.height > 1080 ? globalThis.screen.height : 1080 - }, - optional: [] - } - }; - - if (!!canRequestAudioTrack) { - screen_constraints.audio = { - mandatory: { - chromeMediaSource: error ? 'screen' : 'desktop' - // echoCancellation: true - }, - optional: [] - }; - } - - if (sourceId) { - screen_constraints.video.mandatory.chromeMediaSourceId = sourceId; - - if (screen_constraints.audio && screen_constraints.audio.mandatory) { - screen_constraints.audio.mandatory.chromeMediaSourceId = sourceId; - } - } - - return screen_constraints; -} - -function postGetSourceIdMessage(custom_parameter) { - if (!iframe) { - loadIFrame(function () { - postGetSourceIdMessage(custom_parameter); - }); - return; - } - - if (!iframe.isLoaded) { - setTimeout(function () { - postGetSourceIdMessage(custom_parameter); - }, 100); - return; - } - - if (!custom_parameter) { - iframe.contentWindow.postMessage( - { - captureSourceId: true - }, - '*' - ); - } else if (!!custom_parameter.forEach) { - iframe.contentWindow.postMessage( - { - captureCustomSourceId: custom_parameter - }, - '*' - ); - } else { - iframe.contentWindow.postMessage( - { - captureSourceIdWithAudio: true - }, - '*' - ); - } -} - -var iframe; - -// this function is used in RTCMultiConnection v3 -globalThis.getScreenConstraints = function (callback) { - loadIFrame(function () { - getScreenId(function (error, sourceId, screen_constraints) { - if (!screen_constraints) { - screen_constraints = { - video: true - }; - } - - callback(error, screen_constraints.video); - }); - }); -}; - -function loadIFrame(loadCallback) { - if (iframe) { - loadCallback(); - return; - } - - iframe = document.createElement('iframe'); - iframe.onload = function () { - iframe.isLoaded = true; - loadCallback(); - }; - iframe.src = 'https://openvidu.github.io/openvidu-screen-sharing-chrome-extension/'; - iframe.style.display = 'none'; - (document.body || document.documentElement).appendChild(iframe); -} - -globalThis.getChromeExtensionStatus = function (callback) { - // for Firefox: - if (!!navigator.mozGetUserMedia) { - callback('installed-enabled'); - return; - } - - globalThis.addEventListener('message', onIFrameCallback); - - function onIFrameCallback(event) { - if (!event.data) return; - - if (event.data.chromeExtensionStatus) { - callback(event.data.chromeExtensionStatus); - - // this event listener is no more needed - globalThis.removeEventListener('message', onIFrameCallback); - } - } - - setTimeout(postGetChromeExtensionStatusMessage, 100); -}; - -function postGetChromeExtensionStatusMessage() { - if (!iframe) { - loadIFrame(postGetChromeExtensionStatusMessage); - return; - } - - if (!iframe.isLoaded) { - setTimeout(postGetChromeExtensionStatusMessage, 100); - return; - } - - iframe.contentWindow.postMessage( - { - getChromeExtensionStatus: true - }, - '*' - ); -} - -exports.getScreenId = globalThis.getScreenId; diff --git a/openvidu-browser/src/OpenViduInternal/ScreenSharing/Screen-Capturing.js b/openvidu-browser/src/OpenViduInternal/ScreenSharing/Screen-Capturing.js deleted file mode 100644 index 0eb3cfd3..00000000 --- a/openvidu-browser/src/OpenViduInternal/ScreenSharing/Screen-Capturing.js +++ /dev/null @@ -1,162 +0,0 @@ -// global variables -var chromeMediaSource = 'screen'; -var sourceId; -var screenCallback; - -if (typeof window !== 'undefined' && typeof navigator !== 'undefined' && typeof navigator.userAgent !== 'undefined') { - var isFirefox = typeof window.InstallTrigger !== 'undefined'; - var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0; - var isChrome = !!window.chrome && !isOpera; - - window.addEventListener('message', function (event) { - if (event.origin != window.location.origin) { - return; - } - onMessageCallback(event.data); - }); -} - -// and the function that handles received messages -function onMessageCallback(data) { - // "cancel" button is clicked - if (data == 'PermissionDeniedError') { - if (screenCallback) return screenCallback('PermissionDeniedError'); - else throw new Error('PermissionDeniedError'); - } - // extension notified his presence - if (data == 'rtcmulticonnection-extension-loaded') { - chromeMediaSource = 'desktop'; - } - // extension shared temp sourceId - if (data.sourceId && screenCallback) { - screenCallback((sourceId = data.sourceId), data.canRequestAudioTrack === true); - } -} - -// this method can be used to check if chrome extension is installed & enabled. -function isChromeExtensionAvailable(callback) { - if (!callback) return; - if (chromeMediaSource == 'desktop') return callback(true); - - // ask extension if it is available - window.postMessage('are-you-there', '*'); - setTimeout(function () { - if (chromeMediaSource == 'screen') { - callback(false); - } else callback(true); - }, 2000); -} - -// this function can be used to get "source-id" from the extension -function getSourceId(callback) { - if (!callback) throw '"callback" parameter is mandatory.'; - if (sourceId) return callback(sourceId); - screenCallback = callback; - window.postMessage('get-sourceId', '*'); -} - -// this function can be used to get "source-id" from the extension -function getCustomSourceId(arr, callback) { - if (!arr || !arr.forEach) throw '"arr" parameter is mandatory and it must be an array.'; - if (!callback) throw '"callback" parameter is mandatory.'; - - if (sourceId) return callback(sourceId); - - screenCallback = callback; - window.postMessage( - { - 'get-custom-sourceId': arr - }, - '*' - ); -} - -// this function can be used to get "source-id" from the extension -function getSourceIdWithAudio(callback) { - if (!callback) throw '"callback" parameter is mandatory.'; - if (sourceId) return callback(sourceId); - - screenCallback = callback; - window.postMessage('audio-plus-tab', '*'); -} - -function getChromeExtensionStatus(extensionid, callback) { - if (isFirefox) return callback('not-chrome'); - if (arguments.length != 2) { - callback = extensionid; - extensionid = 'lfcgfepafnobdloecchnfaclibenjold'; // default extension-id - } - var image = document.createElement('img'); - image.src = 'chrome-extension://' + extensionid + '/icon.png'; - image.onload = function () { - chromeMediaSource = 'screen'; - window.postMessage('are-you-there', '*'); - setTimeout(function () { - if (chromeMediaSource == 'screen') { - callback('installed-disabled'); - } else callback('installed-enabled'); - }, 2000); - }; - image.onerror = function () { - callback('not-installed'); - }; -} - -function getScreenConstraintsWithAudio(callback) { - getScreenConstraints(callback, true); -} - -// this function explains how to use above methods/objects -function getScreenConstraints(callback, captureSourceIdWithAudio) { - sourceId = ''; - var firefoxScreenConstraints = { - mozMediaSource: 'window', - mediaSource: 'window' - }; - if (isFirefox) return callback(null, firefoxScreenConstraints); - // this statement defines getUserMedia constraints - // that will be used to capture content of screen - var screen_constraints = { - mandatory: { - chromeMediaSource: chromeMediaSource, - maxWidth: screen.width > 1920 ? screen.width : 1920, - maxHeight: screen.height > 1080 ? screen.height : 1080 - }, - optional: [] - }; - // this statement verifies chrome extension availability - // if installed and available then it will invoke extension API - // otherwise it will fallback to command-line based screen capturing API - if (chromeMediaSource == 'desktop' && !sourceId) { - if (captureSourceIdWithAudio) { - getSourceIdWithAudio(function (sourceId, canRequestAudioTrack) { - screen_constraints.mandatory.chromeMediaSourceId = sourceId; - - if (canRequestAudioTrack) { - screen_constraints.canRequestAudioTrack = true; - } - callback(sourceId == 'PermissionDeniedError' ? sourceId : null, screen_constraints); - }); - } else { - getSourceId(function (sourceId) { - screen_constraints.mandatory.chromeMediaSourceId = sourceId; - callback(sourceId == 'PermissionDeniedError' ? sourceId : null, screen_constraints); - }); - } - return; - } - - // this statement sets gets 'sourceId" and sets "chromeMediaSourceId" - if (chromeMediaSource == 'desktop') { - screen_constraints.mandatory.chromeMediaSourceId = sourceId; - } - - // now invoking native getUserMedia API - callback(null, screen_constraints); -} - -exports.getScreenConstraints = getScreenConstraints; -exports.getScreenConstraintsWithAudio = getScreenConstraintsWithAudio; -exports.isChromeExtensionAvailable = isChromeExtensionAvailable; -exports.getChromeExtensionStatus = getChromeExtensionStatus; -exports.getSourceId = getSourceId; diff --git a/openvidu-browser/src/OpenViduInternal/Utils/Platform.ts b/openvidu-browser/src/OpenViduInternal/Utils/Platform.ts deleted file mode 100644 index 7cf6e301..00000000 --- a/openvidu-browser/src/OpenViduInternal/Utils/Platform.ts +++ /dev/null @@ -1,233 +0,0 @@ -import platform = require('platform'); - -export class PlatformUtils { - protected static instance: PlatformUtils; - constructor() {} - - static getInstance(): PlatformUtils { - if (!this.instance) { - this.instance = new PlatformUtils(); - } - return PlatformUtils.instance; - } - - public isChromeBrowser(): boolean { - return platform.name === 'Chrome'; - } - - /** - * @hidden - */ - public isSafariBrowser(): boolean { - return platform.name === 'Safari'; - } - - /** - * @hidden - */ - public isChromeMobileBrowser(): boolean { - return platform.name === 'Chrome Mobile'; - } - - /** - * @hidden - */ - public isFirefoxBrowser(): boolean { - return platform.name === 'Firefox'; - } - - /** - * @hidden - */ - public isFirefoxMobileBrowser(): boolean { - return platform.name === 'Firefox Mobile' || platform.name === 'Firefox for iOS'; - } - - /** - * @hidden - */ - public isOperaBrowser(): boolean { - return platform.name === 'Opera'; - } - - /** - * @hidden - */ - public isOperaMobileBrowser(): boolean { - return platform.name === 'Opera Mobile'; - } - - /** - * @hidden - */ - public isEdgeBrowser(): boolean { - const version = platform?.version ? parseFloat(platform.version) : -1; - return platform.name === 'Microsoft Edge' && version >= 80; - } - - /** - * @hidden - */ - public isEdgeMobileBrowser(): boolean { - const version = platform?.version ? parseFloat(platform.version) : -1; - return platform.name === 'Microsoft Edge' && (platform.os?.family === 'Android' || platform.os?.family === 'iOS') && version > 45; - } - - /** - * @hidden - */ - public isAndroidBrowser(): boolean { - return platform.name === 'Android Browser'; - } - - /** - * @hidden - */ - public isElectron(): boolean { - return platform.name === 'Electron'; - } - - /** - * @hidden - */ - public isNodeJs(): boolean { - return platform.name === 'Node.js'; - } - - /** - * @hidden - */ - public isSamsungBrowser(): boolean { - return platform.name === 'Samsung Internet Mobile' || platform.name === 'Samsung Internet'; - } - - // TODO: This method exists to overcome bug https://github.com/bestiejs/platform.js/issues/184 - /** - * @hidden - */ - public isMotorolaEdgeDevice(): boolean { - return platform.product?.toLowerCase().includes('motorola edge') || false; - } - - /** - * @hidden - */ - public isIPhoneOrIPad(): boolean { - const userAgent = !!platform.ua ? platform.ua : navigator.userAgent; - const isTouchable = 'ontouchend' in document; - const isIPad = /\b(\w*Macintosh\w*)\b/.test(userAgent) && isTouchable; - const isIPhone = /\b(\w*iPhone\w*)\b/.test(userAgent) && /\b(\w*Mobile\w*)\b/.test(userAgent) && isTouchable; - return isIPad || isIPhone; - } - - /** - * @hidden - */ - public isIOSWithSafari(): boolean { - const userAgent = !!platform.ua ? platform.ua : navigator.userAgent; - return ( - this.isIPhoneOrIPad() && - /\b(\w*Apple\w*)\b/.test(navigator.vendor) && - /\b(\w*Safari\w*)\b/.test(userAgent) && - !/\b(\w*CriOS\w*)\b/.test(userAgent) && - !/\b(\w*FxiOS\w*)\b/.test(userAgent) - ); - } - - /** - * @hidden - */ - public isIonicIos(): boolean { - return this.isIPhoneOrIPad() && platform.ua!!.indexOf('Safari') === -1; - } - - /** - * @hidden - */ - public isIonicAndroid(): boolean { - return platform.os!!.family === 'Android' && platform.name == 'Android Browser'; - } - - /** - * @hidden - */ - public isMobileDevice(): boolean { - return platform.os!!.family === 'iOS' || platform.os!!.family === 'Android'; - } - - /** - * @hidden - */ - public isReactNative(): boolean { - return false; - } - - /** - * @hidden - */ - public isChromium(): boolean { - return ( - this.isChromeBrowser() || - this.isChromeMobileBrowser() || - this.isOperaBrowser() || - this.isOperaMobileBrowser() || - this.isEdgeBrowser() || - this.isEdgeMobileBrowser() || - this.isSamsungBrowser() || - this.isIonicAndroid() || - this.isIonicIos() || - this.isElectron() || - // TODO: remove when possible - this.isMotorolaEdgeDevice() - ); - } - - - - /** - * @hidden - */ - public canScreenShare(): boolean { - const version = platform?.version ? parseFloat(platform.version) : -1; - // Reject mobile devices - if (this.isMobileDevice()) { - return false; - } - return ( - this.isChromeBrowser() || - this.isFirefoxBrowser() || - this.isOperaBrowser() || - this.isElectron() || - this.isEdgeBrowser() || - (this.isSafariBrowser() && version >= 13) - ); - } - - /** - * @hidden - */ - public getName(): string { - return platform.name || ''; - } - - /** - * @hidden - */ - public getVersion(): string { - return platform.version || ''; - } - - /** - * @hidden - */ - public getFamily(): string { - return platform.os!!.family || ''; - } - - /** - * @hidden - */ - public getDescription(): string { - return platform.description || ''; - } -} diff --git a/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts b/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts deleted file mode 100644 index 3df54948..00000000 --- a/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts +++ /dev/null @@ -1,600 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import freeice = require('freeice'); -import { v4 as uuidv4 } from 'uuid'; -import { TypeOfVideo } from '../Enums/TypeOfVideo'; -import { ExceptionEventName } from '../Events/ExceptionEvent'; -import { OpenViduLogger } from '../Logger/OpenViduLogger'; -import { PlatformUtils } from '../Utils/Platform'; - -/** - * @hidden - */ -const logger: OpenViduLogger = OpenViduLogger.getInstance(); -/** - * @hidden - */ -let platform: PlatformUtils; - -export interface WebRtcPeerConfiguration { - mediaConstraints: { - audio: boolean; - video: boolean; - }; - simulcast: boolean; - mediaServer: string; - onIceCandidate: (event: RTCIceCandidate) => void; - onIceConnectionStateException: (exceptionName: ExceptionEventName, message: string, data?: any) => void; - iceServers?: RTCIceServer[]; - rtcConfiguration?: RTCConfiguration; - mediaStream?: MediaStream | null; - mode?: 'sendonly' | 'recvonly' | 'sendrecv'; - id?: string; - typeOfVideo: TypeOfVideo | undefined; -} - -export class WebRtcPeer { - pc: RTCPeerConnection; - remoteCandidatesQueue: RTCIceCandidate[] = []; - localCandidatesQueue: RTCIceCandidate[] = []; - - // Same as WebRtcPeerConfiguration but without optional fields. - protected configuration: Required; - - private iceCandidateList: RTCIceCandidate[] = []; - - constructor(configuration: WebRtcPeerConfiguration) { - platform = PlatformUtils.getInstance(); - - this.configuration = { - ...configuration, - iceServers: !!configuration.iceServers && configuration.iceServers.length > 0 ? configuration.iceServers : freeice(), - rtcConfiguration: configuration.rtcConfiguration !== undefined ? configuration.rtcConfiguration : {}, - mediaStream: configuration.mediaStream !== undefined ? configuration.mediaStream : null, - mode: !!configuration.mode ? configuration.mode : 'sendrecv', - id: !!configuration.id ? configuration.id : this.generateUniqueId() - }; - // prettier-ignore - logger.debug(`[WebRtcPeer] configuration:\n${JSON.stringify(this.configuration, null, 2)}`); - - let rtcConfiguration: RTCConfiguration = this.configuration.rtcConfiguration - ? this.configuration.rtcConfiguration - : { iceServers: this.configuration.iceServers }; - if (!rtcConfiguration.iceServers && this.configuration.iceServers) { - rtcConfiguration.iceServers = this.configuration.iceServers; - } - this.pc = new RTCPeerConnection(rtcConfiguration); - - this.pc.addEventListener('icecandidate', (event: RTCPeerConnectionIceEvent) => { - if (event.candidate !== null) { - // `RTCPeerConnectionIceEvent.candidate` is supposed to be an RTCIceCandidate: - // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnectioniceevent-candidate - // - // But in practice, it is actually an RTCIceCandidateInit that can be used to - // obtain a proper candidate, using the RTCIceCandidate constructor: - // https://w3c.github.io/webrtc-pc/#dom-rtcicecandidate-constructor - const candidateInit: RTCIceCandidateInit = event.candidate as RTCIceCandidateInit; - const iceCandidate = new RTCIceCandidate(candidateInit); - - this.configuration.onIceCandidate(iceCandidate); - if (iceCandidate.candidate !== '') { - this.localCandidatesQueue.push(iceCandidate); - } - } - }); - - this.pc.addEventListener('signalingstatechange', async () => { - if (this.pc.signalingState === 'stable') { - // SDP Offer/Answer finished. Add stored remote candidates. - while (this.iceCandidateList.length > 0) { - let candidate = this.iceCandidateList.shift(); - try { - await this.pc.addIceCandidate(candidate); - } catch (error) { - logger.error('Error when calling RTCPeerConnection#addIceCandidate for RTCPeerConnection ' + this.getId(), error); - } - } - } - }); - } - - getId(): string { - return this.configuration.id; - } - - /** - * This method frees the resources used by WebRtcPeer - */ - dispose() { - logger.debug('Disposing WebRtcPeer'); - if (this.pc) { - if (this.pc.signalingState === 'closed') { - return; - } - this.pc.close(); - this.remoteCandidatesQueue = []; - this.localCandidatesQueue = []; - } - } - - // DEPRECATED LEGACY METHOD: Old WebRTC versions don't implement - // Transceivers, and instead depend on the deprecated - // "offerToReceiveAudio" and "offerToReceiveVideo". - private createOfferLegacy(): Promise { - if (!!this.configuration.mediaStream) { - this.deprecatedPeerConnectionTrackApi(); - } - - const hasAudio = this.configuration.mediaConstraints.audio; - const hasVideo = this.configuration.mediaConstraints.video; - - const options: RTCOfferOptions = { - offerToReceiveAudio: this.configuration.mode !== 'sendonly' && hasAudio, - offerToReceiveVideo: this.configuration.mode !== 'sendonly' && hasVideo - }; - - logger.debug('[createOfferLegacy] RTCPeerConnection.createOffer() options:', JSON.stringify(options)); - - return this.pc.createOffer(options); - } - - /** - * Creates an SDP offer from the local RTCPeerConnection to send to the other peer. - * Only if the negotiation was initiated by this peer. - */ - async createOffer(): Promise { - // TODO: Delete this conditional when all supported browsers are - // modern enough to implement the Transceiver methods. - if (!('addTransceiver' in this.pc)) { - logger.warn( - '[createOffer] Method RTCPeerConnection.addTransceiver() is NOT available; using LEGACY offerToReceive{Audio,Video}' - ); - return this.createOfferLegacy(); - } else { - logger.debug('[createOffer] Method RTCPeerConnection.addTransceiver() is available; using it'); - } - - // Spec doc: https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtransceiver - - if (this.configuration.mode !== 'recvonly') { - // To send media, assume that all desired media tracks have been - // already added by higher level code to our MediaStream. - - if (!this.configuration.mediaStream) { - throw new Error( - `[WebRtcPeer.createOffer] Direction is '${this.configuration.mode}', but no stream was configured to be sent` - ); - } - - for (const track of this.configuration.mediaStream.getTracks()) { - const tcInit: RTCRtpTransceiverInit = { - direction: this.configuration.mode, - streams: [this.configuration.mediaStream] - }; - - if (track.kind === 'video' && this.configuration.simulcast) { - // Check if the requested size is enough to ask for 3 layers. - const trackSettings = track.getSettings(); - const trackConsts = track.getConstraints(); - - const trackWidth: number = - trackSettings.width ?? (trackConsts.width as ConstrainULongRange).ideal ?? (trackConsts.width as number) ?? 0; - const trackHeight: number = - trackSettings.height ?? (trackConsts.height as ConstrainULongRange).ideal ?? (trackConsts.height as number) ?? 0; - logger.info(`[createOffer] Video track dimensions: ${trackWidth}x${trackHeight}`); - - const trackPixels = trackWidth * trackHeight; - let maxLayers = 0; - if (trackPixels >= 960 * 540) { - maxLayers = 3; - } else if (trackPixels >= 480 * 270) { - maxLayers = 2; - } else { - maxLayers = 1; - } - - tcInit.sendEncodings = []; - for (let l = 0; l < maxLayers; l++) { - const layerDiv = 2 ** (maxLayers - l - 1); - - const encoding: RTCRtpEncodingParameters = { - rid: 'rdiv' + layerDiv.toString(), - - // @ts-ignore -- Property missing from DOM types. - scalabilityMode: 'L1T1' - }; - - if (['detail', 'text'].includes(track.contentHint)) { - // Prioritize best resolution, for maximum picture detail. - encoding.scaleResolutionDownBy = 1.0; - - // @ts-ignore -- Property missing from DOM types. - encoding.maxFramerate = Math.floor(30 / layerDiv); - } else { - encoding.scaleResolutionDownBy = layerDiv; - } - - tcInit.sendEncodings.push(encoding); - } - } - - const tc = this.pc.addTransceiver(track, tcInit); - - if (track.kind === 'video') { - let sendParams = tc.sender.getParameters(); - let needSetParams = false; - - if (!sendParams.degradationPreference?.length) { - // degradationPreference for video: "balanced", "maintain-framerate", "maintain-resolution". - // https://www.w3.org/TR/2018/CR-webrtc-20180927/#dom-rtcdegradationpreference - if (['detail', 'text'].includes(track.contentHint)) { - sendParams.degradationPreference = 'maintain-resolution'; - } else { - sendParams.degradationPreference = 'balanced'; - } - - logger.info(`[createOffer] Video sender Degradation Preference set: ${sendParams.degradationPreference}`); - - // FIXME: Firefox implements degradationPreference on each individual encoding! - // (set it on every element of the sendParams.encodings array) - - needSetParams = true; - } - - // FIXME: Check that the simulcast encodings were applied. - // Firefox doesn't implement `RTCRtpTransceiverInit.sendEncodings` - // so the only way to enable simulcast is with `RTCRtpSender.setParameters()`. - // - // This next block can be deleted when Firefox fixes bug #1396918: - // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918 - // - // NOTE: This is done in a way that is compatible with all browsers, to save on - // browser-conditional code. The idea comes from WebRTC Adapter.js: - // * https://github.com/webrtcHacks/adapter/issues/998 - // * https://github.com/webrtcHacks/adapter/blob/v7.7.0/src/js/firefox/firefox_shim.js#L231-L255 - if (this.configuration.simulcast) { - if (sendParams.encodings?.length !== tcInit.sendEncodings!.length) { - sendParams.encodings = tcInit.sendEncodings!; - - needSetParams = true; - } - } - - if (needSetParams) { - logger.debug(`[createOffer] Setting new RTCRtpSendParameters to video sender`); - try { - await tc.sender.setParameters(sendParams); - } catch (error) { - let message = `[WebRtcPeer.createOffer] Cannot set RTCRtpSendParameters to video sender`; - if (error instanceof Error) { - message += `: ${error.message}`; - } - throw new Error(message); - } - } - } - - // DEBUG: Uncomment for details. - // if (track.kind === "video" && this.configuration.simulcast) { - // // Print browser capabilities. - // // prettier-ignore - // logger.debug(`[createOffer] Transceiver send capabilities (static):\n${JSON.stringify(RTCRtpSender.getCapabilities?.("video"), null, 2)}`); - // // prettier-ignore - // logger.debug(`[createOffer] Transceiver recv capabilities (static):\n${JSON.stringify(RTCRtpReceiver.getCapabilities?.("video"), null, 2)}`); - - // // Print requested Transceiver encodings and parameters. - // // prettier-ignore - // logger.debug(`[createOffer] Transceiver send encodings (requested):\n${JSON.stringify(tcInit.sendEncodings, null, 2)}`); - // // prettier-ignore - // logger.debug(`[createOffer] Transceiver send parameters (accepted):\n${JSON.stringify(tc.sender.getParameters(), null, 2)}`); - // } - } - } else { - // To just receive media, create new recvonly transceivers. - for (const kind of ['audio', 'video']) { - // Check if the media kind should be used. - if (!this.configuration.mediaConstraints[kind]) { - continue; - } - - this.configuration.mediaStream = new MediaStream(); - this.pc.addTransceiver(kind, { - direction: this.configuration.mode, - streams: [this.configuration.mediaStream] - }); - } - } - - let sdpOffer: RTCSessionDescriptionInit; - try { - sdpOffer = await this.pc.createOffer(); - } catch (error) { - let message = `[WebRtcPeer.createOffer] Browser failed creating an SDP Offer`; - if (error instanceof Error) { - message += `: ${error.message}`; - } - throw new Error(message); - } - - return sdpOffer; - } - - deprecatedPeerConnectionTrackApi() { - for (const track of this.configuration.mediaStream!.getTracks()) { - this.pc.addTrack(track, this.configuration.mediaStream!); - } - } - - /** - * Creates an SDP answer from the local RTCPeerConnection to send to the other peer - * Only if the negotiation was initiated by the other peer - */ - createAnswer(): Promise { - return new Promise((resolve, reject) => { - // TODO: Delete this conditional when all supported browsers are - // modern enough to implement the Transceiver methods. - if ('getTransceivers' in this.pc) { - logger.debug('[createAnswer] Method RTCPeerConnection.getTransceivers() is available; using it'); - - // Ensure that the PeerConnection already contains one Transceiver - // for each kind of media. - // The Transceivers should have been already created internally by - // the PC itself, when `pc.setRemoteDescription(sdpOffer)` was called. - - for (const kind of ['audio', 'video']) { - // Check if the media kind should be used. - if (!this.configuration.mediaConstraints[kind]) { - continue; - } - - let tc = this.pc.getTransceivers().find((tc) => tc.receiver.track.kind === kind); - - if (tc) { - // Enforce our desired direction. - tc.direction = this.configuration.mode; - } else { - return reject(new Error(`${kind} requested, but no transceiver was created from remote description`)); - } - } - - this.pc - .createAnswer() - .then((sdpAnswer) => resolve(sdpAnswer)) - .catch((error) => reject(error)); - } else { - // TODO: Delete else branch when all supported browsers are - // modern enough to implement the Transceiver methods - - let offerAudio, - offerVideo = true; - if (!!this.configuration.mediaConstraints) { - offerAudio = - typeof this.configuration.mediaConstraints.audio === 'boolean' ? this.configuration.mediaConstraints.audio : true; - offerVideo = - typeof this.configuration.mediaConstraints.video === 'boolean' ? this.configuration.mediaConstraints.video : true; - const constraints: RTCOfferOptions = { - offerToReceiveAudio: offerAudio, - offerToReceiveVideo: offerVideo - }; - (this.pc as RTCPeerConnection).createAnswer(constraints) - .then((sdpAnswer) => resolve(sdpAnswer)) - .catch((error) => reject(error)); - } - } - - // else, there is nothing to do; the legacy createAnswer() options do - // not offer any control over which tracks are included in the answer. - }); - } - - /** - * This peer initiated negotiation. Step 1/4 of SDP offer-answer protocol - */ - processLocalOffer(offer: RTCSessionDescriptionInit): Promise { - return new Promise((resolve, reject) => { - this.pc - .setLocalDescription(offer) - .then(() => { - const localDescription = this.pc.localDescription; - if (!!localDescription) { - logger.debug('Local description set', localDescription.sdp); - return resolve(); - } else { - return reject('Local description is not defined'); - } - }) - .catch((error) => reject(error)); - }); - } - - /** - * Other peer initiated negotiation. Step 2/4 of SDP offer-answer protocol - */ - processRemoteOffer(sdpOffer: string): Promise { - return new Promise((resolve, reject) => { - const offer: RTCSessionDescriptionInit = { - type: 'offer', - sdp: sdpOffer - }; - logger.debug('SDP offer received, setting remote description', offer); - - if (this.pc.signalingState === 'closed') { - return reject('RTCPeerConnection is closed when trying to set remote description'); - } - this.setRemoteDescription(offer) - .then(() => resolve()) - .catch((error) => reject(error)); - }); - } - - /** - * Other peer initiated negotiation. Step 3/4 of SDP offer-answer protocol - */ - processLocalAnswer(answer: RTCSessionDescriptionInit): Promise { - return new Promise((resolve, reject) => { - logger.debug('SDP answer created, setting local description'); - if (this.pc.signalingState === 'closed') { - return reject('RTCPeerConnection is closed when trying to set local description'); - } - this.pc - .setLocalDescription(answer) - .then(() => resolve()) - .catch((error) => reject(error)); - }); - } - - /** - * This peer initiated negotiation. Step 4/4 of SDP offer-answer protocol - */ - processRemoteAnswer(sdpAnswer: string): Promise { - return new Promise((resolve, reject) => { - const answer: RTCSessionDescriptionInit = { - type: 'answer', - sdp: sdpAnswer - }; - logger.debug('SDP answer received, setting remote description'); - - if (this.pc.signalingState === 'closed') { - return reject('RTCPeerConnection is closed when trying to set remote description'); - } - this.setRemoteDescription(answer) - .then(() => { - // DEBUG: Uncomment for details. - // { - // const tc = this.pc.getTransceivers().find((tc) => tc.sender.track?.kind === "video"); - // // prettier-ignore - // logger.debug(`[processRemoteAnswer] Transceiver send parameters (effective):\n${JSON.stringify(tc?.sender.getParameters(), null, 2)}`); - // } - - resolve(); - }) - .catch((error) => reject(error)); - }); - } - - /** - * @hidden - */ - async setRemoteDescription(sdp: RTCSessionDescriptionInit): Promise { - return this.pc.setRemoteDescription(sdp); - } - - /** - * Callback function invoked when an ICE candidate is received - */ - addIceCandidate(iceCandidate: RTCIceCandidate): Promise { - return new Promise((resolve, reject) => { - logger.debug('Remote ICE candidate received', iceCandidate); - this.remoteCandidatesQueue.push(iceCandidate); - switch (this.pc.signalingState) { - case 'closed': - reject(new Error('PeerConnection object is closed')); - break; - case 'stable': - if (!!this.pc.remoteDescription) { - this.pc - .addIceCandidate(iceCandidate) - .then(() => resolve()) - .catch((error) => reject(error)); - } else { - this.iceCandidateList.push(iceCandidate); - resolve(); - } - break; - default: - this.iceCandidateList.push(iceCandidate); - resolve(); - } - }); - } - - addIceConnectionStateChangeListener(otherId: string) { - this.pc.addEventListener('iceconnectionstatechange', () => { - const iceConnectionState: RTCIceConnectionState = this.pc.iceConnectionState; - switch (iceConnectionState) { - case 'disconnected': - // Possible network disconnection - const msg1 = - 'IceConnectionState of RTCPeerConnection ' + - this.configuration.id + - ' (' + - otherId + - ') change to "disconnected". Possible network disconnection'; - logger.warn(msg1); - this.configuration.onIceConnectionStateException(ExceptionEventName.ICE_CONNECTION_DISCONNECTED, msg1); - break; - case 'failed': - const msg2 = 'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') to "failed"'; - logger.error(msg2); - this.configuration.onIceConnectionStateException(ExceptionEventName.ICE_CONNECTION_FAILED, msg2); - break; - case 'closed': - logger.log( - 'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "closed"' - ); - break; - case 'new': - logger.log('IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "new"'); - break; - case 'checking': - logger.log( - 'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "checking"' - ); - break; - case 'connected': - logger.log( - 'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "connected"' - ); - break; - case 'completed': - logger.log( - 'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "completed"' - ); - break; - } - }); - } - - /** - * @hidden - */ - generateUniqueId(): string { - return uuidv4(); - } -} - -export class WebRtcPeerRecvonly extends WebRtcPeer { - constructor(configuration: WebRtcPeerConfiguration) { - configuration.mode = 'recvonly'; - super(configuration); - } -} - -export class WebRtcPeerSendonly extends WebRtcPeer { - constructor(configuration: WebRtcPeerConfiguration) { - configuration.mode = 'sendonly'; - super(configuration); - } -} - -export class WebRtcPeerSendrecv extends WebRtcPeer { - constructor(configuration: WebRtcPeerConfiguration) { - configuration.mode = 'sendrecv'; - super(configuration); - } -} diff --git a/openvidu-browser/src/OpenViduInternal/WebRtcStats/WebRtcStats.ts b/openvidu-browser/src/OpenViduInternal/WebRtcStats/WebRtcStats.ts deleted file mode 100644 index 626deb14..00000000 --- a/openvidu-browser/src/OpenViduInternal/WebRtcStats/WebRtcStats.ts +++ /dev/null @@ -1,471 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// tslint:disable:no-string-literal - -import { Stream } from '../../OpenVidu/Stream'; -import { OpenViduLogger } from '../Logger/OpenViduLogger'; -import { PlatformUtils } from '../Utils/Platform'; -/** - * @hidden - */ -const logger: OpenViduLogger = OpenViduLogger.getInstance(); -/** - * @hidden - */ -let platform: PlatformUtils; - -interface WebrtcStatsConfig { - interval: number; - httpEndpoint: string; -} - -interface JSONStatsResponse { - '@timestamp': string; - participant_id: string; - session_id: string; - platform: string; - platform_description: string; - stream: string; - webrtc_stats: IWebrtcStats; -} - -/** - * Common WebRtcSTats for latest Chromium and Firefox versions - */ -interface IWebrtcStats { - inbound?: { - audio: - | { - bytesReceived: number; - packetsReceived: number; - packetsLost: number; - jitter: number; - } - | {}; - video: - | { - bytesReceived: number; - packetsReceived: number; - packetsLost: number; - jitter?: number; // Firefox - jitterBufferDelay?: number; // Chrome - framesDecoded: number; - firCount: number; - nackCount: number; - pliCount: number; - frameHeight?: number; // Chrome - frameWidth?: number; // Chrome - framesDropped?: number; // Chrome - framesReceived?: number; // Chrome - } - | {}; - }; - outbound?: { - audio: - | { - bytesSent: number; - packetsSent: number; - } - | {}; - video: - | { - bytesSent: number; - packetsSent: number; - firCount: number; - framesEncoded: number; - nackCount: number; - pliCount: number; - qpSum: number; - frameHeight?: number; // Chrome - frameWidth?: number; // Chrome - framesSent?: number; // Chrome - } - | {}; - }; - candidatepair?: { - currentRoundTripTime?: number; // Chrome - availableOutgoingBitrate?: number; //Chrome - // availableIncomingBitrate?: number // No support for any browsers (https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidatePairStats/availableIncomingBitrate) - }; -} - -export class WebRtcStats { - private readonly STATS_ITEM_NAME = 'webrtc-stats-config'; - - private webRtcStatsEnabled = false; - private webRtcStatsIntervalId: NodeJS.Timer; - private statsInterval = 1; - private POST_URL: string; - - constructor(private stream: Stream) { - platform = PlatformUtils.getInstance(); - } - - public isEnabled(): boolean { - return this.webRtcStatsEnabled; - } - - public initWebRtcStats(): void { - let webrtcObj; - // When cross-site (aka third-party) cookies are blocked by the browser, - // accessing localStorage in a third-party iframe throws a DOMException. - try { - webrtcObj = localStorage.getItem(this.STATS_ITEM_NAME); - } - catch(e){} - - if (!!webrtcObj) { - this.webRtcStatsEnabled = true; - const webrtcStatsConfig: WebrtcStatsConfig = JSON.parse(webrtcObj); - // webrtc object found in local storage - logger.warn( - 'WebRtc stats enabled for stream ' + this.stream.streamId + ' of connection ' + this.stream.connection.connectionId - ); - logger.warn('localStorage item: ' + JSON.stringify(webrtcStatsConfig)); - - this.POST_URL = webrtcStatsConfig.httpEndpoint; - this.statsInterval = webrtcStatsConfig.interval; // Interval in seconds - - this.webRtcStatsIntervalId = setInterval(async () => { - await this.sendStatsToHttpEndpoint(); - }, this.statsInterval * 1000); - } else { - logger.debug('WebRtc stats not enabled'); - } - } - - // { - // "localCandidate": { - // "id": "RTCIceCandidate_/r4P1y2Q", - // "timestamp": 1616080155617, - // "type": "local-candidate", - // "transportId": "RTCTransport_0_1", - // "isRemote": false, - // "networkType": "wifi", - // "ip": "123.45.67.89", - // "port": 63340, - // "protocol": "udp", - // "candidateType": "srflx", - // "priority": 1686052607, - // "deleted": false, - // "raw": [ - // "candidate:3345412921 1 udp 1686052607 123.45.67.89 63340 typ srflx raddr 192.168.1.31 rport 63340 generation 0 ufrag 0ZtT network-id 1 network-cost 10", - // "candidate:58094482 1 udp 41885695 98.76.54.32 44431 typ relay raddr 123.45.67.89 rport 63340 generation 0 ufrag 0ZtT network-id 1 network-cost 10" - // ] - // }, - // "remoteCandidate": { - // "id": "RTCIceCandidate_1YO18gph", - // "timestamp": 1616080155617, - // "type": "remote-candidate", - // "transportId": "RTCTransport_0_1", - // "isRemote": true, - // "ip": "12.34.56.78", - // "port": 64989, - // "protocol": "udp", - // "candidateType": "srflx", - // "priority": 1679819263, - // "deleted": false, - // "raw": [ - // "candidate:16 1 UDP 1679819263 12.34.56.78 64989 typ srflx raddr 172.19.0.1 rport 64989", - // "candidate:16 1 UDP 1679819263 12.34.56.78 64989 typ srflx raddr 172.19.0.1 rport 64989" - // ] - // } - // } - // Have been tested in: - // - Linux Desktop: - // - Chrome 89.0.4389.90 - // - Opera 74.0.3911.218 - // - Firefox 86 - // - Microsoft Edge 91.0.825.0 - // - Electron 11.3.0 (Chromium 87.0.4280.141) - // - Windows Desktop: - // - Chrome 89.0.4389.90 - // - Opera 74.0.3911.232 - // - Firefox 86.0.1 - // - Microsoft Edge 89.0.774.54 - // - Electron 11.3.0 (Chromium 87.0.4280.141) - // - MacOS Desktop: - // - Chrome 89.0.4389.90 - // - Firefox 87.0 - // - Opera 75.0.3969.93 - // - Microsoft Edge 89.0.774.57 - // - Safari 14.0 (14610.1.28.1.9) - // - Electron 11.3.0 (Chromium 87.0.4280.141) - // - Android: - // - Chrome Mobile 89.0.4389.90 - // - Opera 62.3.3146.57763 - // - Firefox Mobile 86.6.1 - // - Microsoft Edge Mobile 46.02.4.5147 - // - Ionic 5 - // - React Native 0.64 - // - iOS: - // - Safari Mobile - // - ¿Ionic? - // - ¿React Native? - public getSelectedIceCandidateInfo(): Promise { - return new Promise(async (resolve, reject) => { - const statsReport: any = await this.stream.getRTCPeerConnection().getStats(); - let transportStat; - const candidatePairs: Map = new Map(); - const localCandidates: Map = new Map(); - const remoteCandidates: Map = new Map(); - statsReport.forEach((stat: any) => { - if (stat.type === 'transport' && (platform.isChromium() || platform.isSafariBrowser() || platform.isReactNative())) { - transportStat = stat; - } - switch (stat.type) { - case 'candidate-pair': - candidatePairs.set(stat.id, stat); - break; - case 'local-candidate': - localCandidates.set(stat.id, stat); - break; - case 'remote-candidate': - remoteCandidates.set(stat.id, stat); - break; - } - }); - let selectedCandidatePair; - if (transportStat != null) { - const selectedCandidatePairId = transportStat.selectedCandidatePairId; - selectedCandidatePair = candidatePairs.get(selectedCandidatePairId); - } else { - // This is basically Firefox - const length = candidatePairs.size; - const iterator = candidatePairs.values(); - for (let i = 0; i < length; i++) { - const candidatePair = iterator.next().value; - if (candidatePair['selected']) { - selectedCandidatePair = candidatePair; - break; - } - } - } - const localCandidateId = selectedCandidatePair.localCandidateId; - const remoteCandidateId = selectedCandidatePair.remoteCandidateId; - let finalLocalCandidate = localCandidates.get(localCandidateId); - if (!!finalLocalCandidate) { - const candList = this.stream.getLocalIceCandidateList(); - const cand = candList.filter((c: RTCIceCandidate) => { - return ( - !!c.candidate && - (c.candidate.indexOf(finalLocalCandidate.ip) >= 0 || c.candidate.indexOf(finalLocalCandidate.address) >= 0) && - c.candidate.indexOf(finalLocalCandidate.port) >= 0 - ); - }); - finalLocalCandidate.raw = []; - for (let c of cand) { - finalLocalCandidate.raw.push(c.candidate); - } - } else { - finalLocalCandidate = 'ERROR: No active local ICE candidate. Probably ICE-TCP is being used'; - } - - let finalRemoteCandidate = remoteCandidates.get(remoteCandidateId); - if (!!finalRemoteCandidate) { - const candList = this.stream.getRemoteIceCandidateList(); - const cand = candList.filter((c: RTCIceCandidate) => { - return ( - !!c.candidate && - (c.candidate.indexOf(finalRemoteCandidate.ip) >= 0 || c.candidate.indexOf(finalRemoteCandidate.address) >= 0) && - c.candidate.indexOf(finalRemoteCandidate.port) >= 0 - ); - }); - finalRemoteCandidate.raw = []; - for (let c of cand) { - finalRemoteCandidate.raw.push(c.candidate); - } - } else { - finalRemoteCandidate = 'ERROR: No active remote ICE candidate. Probably ICE-TCP is being used'; - } - - return resolve({ - localCandidate: finalLocalCandidate, - remoteCandidate: finalRemoteCandidate - }); - }); - } - - public stopWebRtcStats() { - if (this.webRtcStatsEnabled) { - clearInterval(this.webRtcStatsIntervalId); - logger.warn( - 'WebRtc stats stopped for disposed stream ' + this.stream.streamId + ' of connection ' + this.stream.connection.connectionId - ); - } - } - - private async sendStats(url: string, response: JSONStatsResponse): Promise { - try { - const configuration: RequestInit = { - headers: { - 'Content-type': 'application/json' - }, - body: JSON.stringify(response), - method: 'POST' - }; - await fetch(url, configuration); - } catch (error) { - logger.error(`sendStats error: ${JSON.stringify(error)}`); - } - } - - private async sendStatsToHttpEndpoint(): Promise { - try { - const webrtcStats: IWebrtcStats = await this.getCommonStats(); - const response = this.generateJSONStatsResponse(webrtcStats); - await this.sendStats(this.POST_URL, response); - } catch (error) { - logger.log(error); - } - } - - // Have been tested in: - // - Linux Desktop: - // - Chrome 89.0.4389.90 - // - Opera 74.0.3911.218 - // - Firefox 86 - // - Microsoft Edge 91.0.825.0 - // - Electron 11.3.0 (Chromium 87.0.4280.141) - // - Windows Desktop: - // - Chrome 89.0.4389.90 - // - Opera 74.0.3911.232 - // - Firefox 86.0.1 - // - Microsoft Edge 89.0.774.54 - // - Electron 11.3.0 (Chromium 87.0.4280.141) - // - MacOS Desktop: - // - Chrome 89.0.4389.90 - // - Opera 75.0.3969.93 - // - Firefox 87.0 - // - Microsoft Edge 89.0.774.57 - // - Safari 14.0 (14610.1.28.1.9) - // - Electron 11.3.0 (Chromium 87.0.4280.141) - // - Android: - // - Chrome Mobile 89.0.4389.90 - // - Opera 62.3.3146.57763 - // - Firefox Mobile 86.6.1 - // - Microsoft Edge Mobile 46.02.4.5147 - // - Ionic 5 - // - React Native 0.64 - // - iOS: - // - Safari Mobile - // - ¿Ionic? - // - ¿React Native? - public async getCommonStats(): Promise { - return new Promise(async (resolve, reject) => { - try { - const statsReport: any = await this.stream.getRTCPeerConnection().getStats(); - const response: IWebrtcStats = this.getWebRtcStatsResponseOutline(); - const videoTrackStats = ['framesReceived', 'framesDropped', 'framesSent', 'frameHeight', 'frameWidth']; - const candidatePairStats = ['availableOutgoingBitrate', 'currentRoundTripTime']; - - statsReport.forEach((stat: any) => { - let mediaType = stat.mediaType != null ? stat.mediaType : stat.kind; - const addStat = (direction: string, key: string): void => { - if (stat[key] != null && response[direction] != null) { - if (!mediaType && videoTrackStats.indexOf(key) > -1) { - mediaType = 'video'; - } - if (direction != null && mediaType != null && key != null && response[direction][mediaType] != null) { - response[direction][mediaType][key] = Number(stat[key]); - } else if (direction != null && key != null && candidatePairStats.includes(key)) { - // candidate-pair-stats - response[direction][key] = Number(stat[key]); - } - } - }; - - switch (stat.type) { - case 'outbound-rtp': - addStat('outbound', 'bytesSent'); - addStat('outbound', 'packetsSent'); - addStat('outbound', 'framesEncoded'); - addStat('outbound', 'nackCount'); - addStat('outbound', 'firCount'); - addStat('outbound', 'pliCount'); - addStat('outbound', 'qpSum'); - break; - case 'inbound-rtp': - addStat('inbound', 'bytesReceived'); - addStat('inbound', 'packetsReceived'); - addStat('inbound', 'packetsLost'); - addStat('inbound', 'jitter'); - addStat('inbound', 'framesDecoded'); - addStat('inbound', 'nackCount'); - addStat('inbound', 'firCount'); - addStat('inbound', 'pliCount'); - break; - case 'track': - addStat('inbound', 'jitterBufferDelay'); - addStat('inbound', 'framesReceived'); - addStat('outbound', 'framesDropped'); - addStat('outbound', 'framesSent'); - addStat(this.stream.isLocal() ? 'outbound' : 'inbound', 'frameHeight'); - addStat(this.stream.isLocal() ? 'outbound' : 'inbound', 'frameWidth'); - break; - case 'candidate-pair': - addStat('candidatepair', 'currentRoundTripTime'); - addStat('candidatepair', 'availableOutgoingBitrate'); - break; - } - }); - - // Delete candidatepair from response if null - if (!response?.candidatepair || Object.keys(response.candidatepair).length === 0) { - delete response.candidatepair; - } - - return resolve(response); - } catch (error) { - logger.error('Error getting common stats: ', error); - return reject(error); - } - }); - } - - private generateJSONStatsResponse(stats: IWebrtcStats): JSONStatsResponse { - return { - '@timestamp': new Date().toISOString(), - participant_id: this.stream.connection.data, - session_id: this.stream.session.sessionId, - platform: platform.getName(), - platform_description: platform.getDescription(), - stream: 'webRTC', - webrtc_stats: stats - }; - } - - private getWebRtcStatsResponseOutline(): IWebrtcStats { - if (this.stream.isLocal()) { - return { - outbound: { - audio: {}, - video: {} - }, - candidatepair: {} - }; - } else { - return { - inbound: { - audio: {}, - video: {} - } - }; - } - } -} diff --git a/openvidu-browser/src/index.ts b/openvidu-browser/src/index.ts deleted file mode 100644 index e4fb3588..00000000 --- a/openvidu-browser/src/index.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { JL } from 'jsnlog'; - -export { OpenVidu } from './OpenVidu/OpenVidu'; -export { Session } from './OpenVidu/Session'; -export { Publisher } from './OpenVidu/Publisher'; -export { Subscriber } from './OpenVidu/Subscriber'; -export { StreamManager } from './OpenVidu/StreamManager'; -export { Stream } from './OpenVidu/Stream'; -export { Connection } from './OpenVidu/Connection'; -export { LocalRecorder } from './OpenVidu/LocalRecorder'; -export { Filter } from './OpenVidu/Filter'; - -export { LocalRecorderState } from './OpenViduInternal/Enums/LocalRecorderState'; -export { OpenViduError, OpenViduErrorName } from './OpenViduInternal/Enums/OpenViduError'; -export { TypeOfVideo } from './OpenViduInternal/Enums/TypeOfVideo'; -export { VideoInsertMode } from './OpenViduInternal/Enums/VideoInsertMode'; - -export { Event } from './OpenViduInternal/Events/Event'; -export { ConnectionEvent } from './OpenViduInternal/Events/ConnectionEvent'; -export { PublisherSpeakingEvent } from './OpenViduInternal/Events/PublisherSpeakingEvent'; -export { RecordingEvent } from './OpenViduInternal/Events/RecordingEvent'; -export { SessionDisconnectedEvent } from './OpenViduInternal/Events/SessionDisconnectedEvent'; -export { SignalEvent } from './OpenViduInternal/Events/SignalEvent'; -export { StreamEvent } from './OpenViduInternal/Events/StreamEvent'; -export { StreamManagerEvent } from './OpenViduInternal/Events/StreamManagerEvent'; -export { VideoElementEvent } from './OpenViduInternal/Events/VideoElementEvent'; -export { StreamPropertyChangedEvent } from './OpenViduInternal/Events/StreamPropertyChangedEvent'; -export { ConnectionPropertyChangedEvent } from './OpenViduInternal/Events/ConnectionPropertyChangedEvent'; -export { FilterEvent } from './OpenViduInternal/Events/FilterEvent'; -export { NetworkQualityLevelChangedEvent } from './OpenViduInternal/Events/NetworkQualityLevelChangedEvent'; -export { SpeechToTextEvent } from './OpenViduInternal/Events/SpeechToTextEvent'; -export { ExceptionEvent, ExceptionEventName } from './OpenViduInternal/Events/ExceptionEvent'; - -export { Capabilities } from './OpenViduInternal/Interfaces/Public/Capabilities'; -export { Device } from './OpenViduInternal/Interfaces/Public/Device'; -export { EventDispatcher } from './OpenVidu/EventDispatcher'; -export { OpenViduAdvancedConfiguration } from './OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration'; -export { PublisherProperties } from './OpenViduInternal/Interfaces/Public/PublisherProperties'; -export { SignalOptions } from './OpenViduInternal/Interfaces/Public/SignalOptions'; -export { StreamManagerVideo } from './OpenViduInternal/Interfaces/Public/StreamManagerVideo'; -export { SubscriberProperties } from './OpenViduInternal/Interfaces/Public/SubscriberProperties'; - -export { EventMap } from './OpenViduInternal/Events/EventMap/EventMap'; -export { SessionEventMap } from './OpenViduInternal/Events/EventMap/SessionEventMap'; -export { StreamManagerEventMap } from './OpenViduInternal/Events/EventMap/StreamManagerEventMap'; -export { PublisherEventMap } from './OpenViduInternal/Events/EventMap/PublisherEventMap'; - -export * from './OpenViduInternal/Events/Types/Types'; - -// Disable jsnlog when library is loaded -JL.setOptions({ enabled: false }); diff --git a/openvidu-browser/tsconfig.json b/openvidu-browser/tsconfig.json deleted file mode 100644 index 6b5bc64b..00000000 --- a/openvidu-browser/tsconfig.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - //"allowUnusedLabels": true, - "allowUnreachableCode": false, - "buildOnSave": false, - "compileOnSave": true, - "compilerOptions": { - "allowJs": true, - "allowSyntheticDefaultImports": true, - "emitBOM": false, - "forceConsistentCasingInFileNames": true, - "lib": [ - "dom", - "es2015.promise", - "es5", - "scripthost" - ], - "module": "commonjs", - "noFallthroughCasesInSwitch": true, - //"noImplicitAny": true, - "noImplicitReturns": true, - "noImplicitThis": true, - //"noUnusedLocals": true, - //"noUnusedParameters": true, - "outDir": "../../lib", - "preserveConstEnums": true, - "removeComments": true, - "rootDir": "./src", - "skipDefaultLibCheck": true, - "skipLibCheck": true, - "sourceMap": true, - "strictNullChecks": true, - "suppressExcessPropertyErrors": true, - "suppressImplicitAnyIndexErrors": true, - "target": "es5" - } -} \ No newline at end of file diff --git a/openvidu-client/.gitignore b/openvidu-client/.gitignore deleted file mode 100644 index b83d2226..00000000 --- a/openvidu-client/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target/ diff --git a/openvidu-client/README.md b/openvidu-client/README.md deleted file mode 100644 index bd12a3e9..00000000 --- a/openvidu-client/README.md +++ /dev/null @@ -1,14 +0,0 @@ -[![License badge](https://img.shields.io/badge/license-Apache2-orange.svg)](http://www.apache.org/licenses/LICENSE-2.0) -[![OpenVidu Tests](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml/badge.svg)](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml) -[![Documentation Status](https://readthedocs.org/projects/openvidu/badge/?version=stable)](https://docs.openvidu.io/en/stable/?badge=stable) -[![Docker badge](https://img.shields.io/docker/pulls/fiware/orion.svg)](https://hub.docker.com/r/openvidu/) -[![Support badge](https://img.shields.io/badge/support-sof-yellowgreen.svg)](https://openvidu.discourse.group/) - -[![][OpenViduLogo]](https://openvidu.io) - -openvidu-client -=== - -Internal Java client used by [openvidu-server](https://github.com/OpenVidu/openvidu/tree/master/openvidu-server). Can be used to implement an Android client. - -[OpenViduLogo]: https://secure.gravatar.com/avatar/5daba1d43042f2e4e85849733c8e5702?s=120 \ No newline at end of file diff --git a/openvidu-client/pom.xml b/openvidu-client/pom.xml deleted file mode 100644 index 35538e63..00000000 --- a/openvidu-client/pom.xml +++ /dev/null @@ -1,106 +0,0 @@ - - 4.0.0 - - - io.openvidu - openvidu-parent - 2.0.0 - - - openvidu-client - 1.1.0 - jar - - OpenVidu Client - - OpenVidu client library for the client-side of OpenVidu Server - - https://github.com/OpenVidu/openvidu - - - - Apache 2.0 - http://www.apache.org/licenses/LICENSE-2.0 - repo - - - - - OpenVidu - https://github.com/OpenVidu/openvidu - - - - ${openvidu.scm.url} - scm:git:${openvidu.scm.connection} - scm:git:${openvidu.scm.connection} - develop - - - - - openvidu.io - -openvidu.io Community - OpenVidu - https://openvidu.io - - - - - - org.kurento - kurento-jsonrpc-client - ${version.kurento} - - - org.kurento - kurento-jsonrpc-client-jetty - ${version.kurento} - - - org.junit.jupiter - junit-jupiter-api - ${version.junit} - test - - - org.mockito - mockito-core - ${version.mockito.core} - test - - - - - - default - - - default - true - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - true - - - - org.apache.maven.plugins - maven-failsafe-plugin - - true - - - - - - - - diff --git a/openvidu-client/src/main/java/io/openvidu/client/OpenViduClient.java b/openvidu-client/src/main/java/io/openvidu/client/OpenViduClient.java deleted file mode 100644 index 0e7aaaab..00000000 --- a/openvidu-client/src/main/java/io/openvidu/client/OpenViduClient.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.openvidu.client; - -import static io.openvidu.client.internal.ProtocolElements.CUSTOMREQUEST_METHOD; -import static io.openvidu.client.internal.ProtocolElements.JOINROOM_METHOD; -import static io.openvidu.client.internal.ProtocolElements.JOINROOM_PEERID_PARAM; -import static io.openvidu.client.internal.ProtocolElements.JOINROOM_PEERSTREAMID_PARAM; -import static io.openvidu.client.internal.ProtocolElements.JOINROOM_PEERSTREAMS_PARAM; -import static io.openvidu.client.internal.ProtocolElements.JOINROOM_ROOM_PARAM; -import static io.openvidu.client.internal.ProtocolElements.JOINROOM_USER_PARAM; -import static io.openvidu.client.internal.ProtocolElements.LEAVEROOM_METHOD; -import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_CANDIDATE_PARAM; -import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_EPNAME_PARAM; -import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_METHOD; -import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_SDPMIDPARAM; -import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_SDPMLINEINDEX_PARAM; -import static io.openvidu.client.internal.ProtocolElements.PUBLISHVIDEO_DOLOOPBACK_PARAM; -import static io.openvidu.client.internal.ProtocolElements.PUBLISHVIDEO_METHOD; -import static io.openvidu.client.internal.ProtocolElements.PUBLISHVIDEO_SDPANSWER_PARAM; -import static io.openvidu.client.internal.ProtocolElements.PUBLISHVIDEO_SDPOFFER_PARAM; -import static io.openvidu.client.internal.ProtocolElements.RECEIVEVIDEO_METHOD; -import static io.openvidu.client.internal.ProtocolElements.RECEIVEVIDEO_SDPANSWER_PARAM; -import static io.openvidu.client.internal.ProtocolElements.RECEIVEVIDEO_SDPOFFER_PARAM; -import static io.openvidu.client.internal.ProtocolElements.RECEIVEVIDEO_SENDER_PARAM; -import static io.openvidu.client.internal.ProtocolElements.SENDMESSAGE_MESSAGE_PARAM; -import static io.openvidu.client.internal.ProtocolElements.SENDMESSAGE_ROOM_METHOD; -import static io.openvidu.client.internal.ProtocolElements.UNPUBLISHVIDEO_METHOD; -import static io.openvidu.client.internal.ProtocolElements.UNSUBSCRIBEFROMVIDEO_METHOD; -import static io.openvidu.client.internal.ProtocolElements.UNSUBSCRIBEFROMVIDEO_SENDER_PARAM; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.eclipse.jetty.util.ssl.SslContextFactory; -import org.kurento.jsonrpc.client.JsonRpcClient; -import org.kurento.jsonrpc.client.JsonRpcClientWebSocket; -import org.kurento.jsonrpc.client.JsonRpcWSConnectionListener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import io.openvidu.client.internal.JsonRoomUtils; -import io.openvidu.client.internal.Notification; - -/** - * Java client for the room server. - * - * @author Radu Tom Vlad - */ -public class OpenViduClient { - - private static final Logger log = LoggerFactory.getLogger(OpenViduClient.class); - - private JsonRpcClient client; - private ServerJsonRpcHandler handler; - - public OpenViduClient(String wsUri) { - this(new JsonRpcClientWebSocket(wsUri, new JsonRpcWSConnectionListener() { - - @Override - public void reconnected(boolean sameServer) { - } - - @Override - public void disconnected() { - log.warn("JsonRpcWebsocket connection: Disconnected"); - } - - @Override - public void connectionFailed() { - log.warn("JsonRpcWebsocket connection: Connection failed"); - } - - @Override - public void connected() { - } - - @Override - public void reconnecting() { - log.warn("JsonRpcWebsocket connection: is reconnecting"); - } - }, new SslContextFactory(true))); - } - - public OpenViduClient(JsonRpcClient client) { - this.client = client; - this.handler = new ServerJsonRpcHandler(); - this.client.setServerRequestHandler(this.handler); - } - - public OpenViduClient(JsonRpcClient client, ServerJsonRpcHandler handler) { - this.client = client; - this.handler = handler; - this.client.setServerRequestHandler(this.handler); - } - - public void close() throws IOException { - this.client.close(); - } - - public Map> joinRoom(String roomName, String userName) - throws IOException { - - JsonObject params = new JsonObject(); - params.addProperty(JOINROOM_ROOM_PARAM, roomName); - params.addProperty(JOINROOM_USER_PARAM, userName); - - JsonElement result = client.sendRequest(JOINROOM_METHOD, params); - Map> peers = new HashMap>(); - JsonArray jsonPeers = JsonRoomUtils.getResponseProperty(result, "value", JsonArray.class); - if (jsonPeers.size() > 0) { - Iterator peerIt = jsonPeers.iterator(); - while (peerIt.hasNext()) { - JsonElement peer = peerIt.next(); - String peerId = JsonRoomUtils.getResponseProperty(peer, JOINROOM_PEERID_PARAM, - String.class); - List streams = new ArrayList(); - JsonArray jsonStreams = JsonRoomUtils.getResponseProperty(peer, JOINROOM_PEERSTREAMS_PARAM, - JsonArray.class, true); - if (jsonStreams != null) { - Iterator streamIt = jsonStreams.iterator(); - while (streamIt.hasNext()) { - streams.add(JsonRoomUtils.getResponseProperty(streamIt.next(), - JOINROOM_PEERSTREAMID_PARAM, String.class)); - } - } - peers.put(peerId, streams); - } - } - return peers; - } - - public void leaveRoom() throws IOException { - client.sendRequest(LEAVEROOM_METHOD, new JsonObject()); - } - - public String publishVideo(String sdpOffer, boolean doLoopback) throws IOException { - JsonObject params = new JsonObject(); - params.addProperty(PUBLISHVIDEO_SDPOFFER_PARAM, sdpOffer); - params.addProperty(PUBLISHVIDEO_DOLOOPBACK_PARAM, doLoopback); - JsonElement result = client.sendRequest(PUBLISHVIDEO_METHOD, params); - return JsonRoomUtils.getResponseProperty(result, PUBLISHVIDEO_SDPANSWER_PARAM, String.class); - } - - public void unpublishVideo() throws IOException { - client.sendRequest(UNPUBLISHVIDEO_METHOD, new JsonObject()); - } - - // sender should look like 'username_streamId' - public String receiveVideoFrom(String sender, String sdpOffer) throws IOException { - JsonObject params = new JsonObject(); - params.addProperty(RECEIVEVIDEO_SENDER_PARAM, sender); - params.addProperty(RECEIVEVIDEO_SDPOFFER_PARAM, sdpOffer); - JsonElement result = client.sendRequest(RECEIVEVIDEO_METHOD, params); - return JsonRoomUtils.getResponseProperty(result, RECEIVEVIDEO_SDPANSWER_PARAM, String.class); - } - - // sender should look like 'username_streamId' - public void unsubscribeFromVideo(String sender) throws IOException { - JsonObject params = new JsonObject(); - params.addProperty(UNSUBSCRIBEFROMVIDEO_SENDER_PARAM, sender); - client.sendRequest(UNSUBSCRIBEFROMVIDEO_METHOD, params); - } - - public void onIceCandidate(String endpointName, String candidate, String sdpMid, - int sdpMLineIndex) throws IOException { - JsonObject params = new JsonObject(); - params.addProperty(ONICECANDIDATE_EPNAME_PARAM, endpointName); - params.addProperty(ONICECANDIDATE_CANDIDATE_PARAM, candidate); - params.addProperty(ONICECANDIDATE_SDPMIDPARAM, sdpMid); - params.addProperty(ONICECANDIDATE_SDPMLINEINDEX_PARAM, sdpMLineIndex); - client.sendRequest(ONICECANDIDATE_METHOD, params); - } - - public void sendMessage(String userName, String roomName, String message) throws IOException { - JsonObject params = new JsonObject(); - params.addProperty(SENDMESSAGE_MESSAGE_PARAM, message); - client.sendRequest(SENDMESSAGE_ROOM_METHOD, params); - } - - public JsonElement customRequest(JsonObject customReqParams) throws IOException { - return client.sendRequest(CUSTOMREQUEST_METHOD, customReqParams); - } - - /** - * Polls the notifications list maintained by this client to obtain new events sent by server. - * This method blocks until there is a notification to return. This is a one-time operation for - * the returned element. - * - * @return a server notification object, null when interrupted while waiting - */ - public Notification getServerNotification() { - return this.handler.getNotification(); - } -} diff --git a/openvidu-client/src/main/java/io/openvidu/client/OpenViduException.java b/openvidu-client/src/main/java/io/openvidu/client/OpenViduException.java deleted file mode 100644 index 4c171929..00000000 --- a/openvidu-client/src/main/java/io/openvidu/client/OpenViduException.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package io.openvidu.client; - -import org.kurento.jsonrpc.JsonRpcErrorException; - -public class OpenViduException extends JsonRpcErrorException { - private static final long serialVersionUID = 1L; - - public static enum Code { - GENERIC_ERROR_CODE(999), WRONG_PATH_CODE(998), WRONG_OPENVIDU_EDITION_ERROR_CODE(997), - SERVICE_NOT_ENABLED_ERROR_CODE(996), - - TRANSPORT_ERROR_CODE(803), TRANSPORT_RESPONSE_ERROR_CODE(802), TRANSPORT_REQUEST_ERROR_CODE(801), - - MEDIA_TYPE_STREAM_INCOMPATIBLE_WITH_RECORDING_PROPERTIES_ERROR_CODE(309), - MEDIA_TYPE_RECORDING_PROPERTIES_ERROR_CODE(308), MEDIA_MUTE_ERROR_CODE(307), - MEDIA_NOT_A_WEB_ENDPOINT_ERROR_CODE(306), MEDIA_RTP_ENDPOINT_ERROR_CODE(305), - MEDIA_WEBRTC_ENDPOINT_ERROR_CODE(304), MEDIA_ENDPOINT_ERROR_CODE(303), MEDIA_SDP_ERROR_CODE(302), - MEDIA_GENERIC_ERROR_CODE(301), - - ROOM_CANNOT_BE_CREATED_ERROR_CODE(204), ROOM_CLOSED_ERROR_CODE(203), ROOM_NOT_FOUND_ERROR_CODE(202), - ROOM_GENERIC_ERROR_CODE(201), - - USER_ALREADY_STREAMING_ERROR_CODE(106), USER_NOT_STREAMING_ERROR_CODE(105), - EXISTING_USER_IN_ROOM_ERROR_CODE(104), USER_CLOSED_ERROR_CODE(103), USER_NOT_FOUND_ERROR_CODE(102), - USER_GENERIC_ERROR_CODE(10), - - USER_UNAUTHORIZED_ERROR_CODE(401), ROLE_NOT_FOUND_ERROR_CODE(402), SESSIONID_CANNOT_BE_CREATED_ERROR_CODE(403), - TOKEN_CANNOT_BE_CREATED_ERROR_CODE(404), EXISTING_FILTER_ALREADY_APPLIED_ERROR_CODE(405), - FILTER_NOT_APPLIED_ERROR_CODE(406), FILTER_EVENT_LISTENER_NOT_FOUND_ERROR_CODE(407), - PUBLISHER_ENDPOINT_NOT_FOUND_ERROR_CODE(408), - - USER_METADATA_FORMAT_INVALID_ERROR_CODE(500), - - SIGNAL_FORMAT_INVALID_ERROR_CODE(600), SIGNAL_TO_INVALID_ERROR_CODE(601), - - BROADCAST_CONCURRENT_ERROR_CODE(711), BROADCAST_START_ERROR_CODE(710), DOCKER_NOT_FOUND(709), RECORDING_PATH_NOT_VALID(708), RECORDING_FILE_EMPTY_ERROR(707), - RECORDING_DELETE_ERROR_CODE(706), RECORDING_LIST_ERROR_CODE(705), RECORDING_STOP_ERROR_CODE(704), - RECORDING_START_ERROR_CODE(703), RECORDING_REPORT_ERROR_CODE(702), RECORDING_COMPLETION_ERROR_CODE(701), - - FORCED_CODEC_NOT_FOUND_IN_SDPOFFER(800), - - MEDIA_NODE_NOT_FOUND(900), MEDIA_NODE_STATUS_WRONG(901), MEDIA_NODE_CONNECTION_ERROR_CODE(902); - - private int value; - - private Code(int value) { - this.value = value; - } - - public int getValue() { - return this.value; - } - } - - private Code code = Code.GENERIC_ERROR_CODE; - - public OpenViduException(Code code, String message) { - super(code.getValue(), message); - this.code = code; - } - - public int getCodeValue() { - return code.getValue(); - } - - @Override - public String toString() { - return super.toString(); - } - -} diff --git a/openvidu-client/src/main/java/io/openvidu/client/ServerJsonRpcHandler.java b/openvidu-client/src/main/java/io/openvidu/client/ServerJsonRpcHandler.java deleted file mode 100644 index 6b6d5bc0..00000000 --- a/openvidu-client/src/main/java/io/openvidu/client/ServerJsonRpcHandler.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.openvidu.client; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; - -import org.kurento.jsonrpc.DefaultJsonRpcHandler; -import org.kurento.jsonrpc.Transaction; -import org.kurento.jsonrpc.message.Request; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import io.openvidu.client.internal.IceCandidate; -import io.openvidu.client.internal.IceCandidateInfo; -import io.openvidu.client.internal.JsonRoomUtils; -import io.openvidu.client.internal.MediaErrorInfo; -import io.openvidu.client.internal.Notification; -import io.openvidu.client.internal.ParticipantEvictedInfo; -import io.openvidu.client.internal.ParticipantJoinedInfo; -import io.openvidu.client.internal.ParticipantLeftInfo; -import io.openvidu.client.internal.ParticipantPublishedInfo; -import io.openvidu.client.internal.ParticipantUnpublishedInfo; -import io.openvidu.client.internal.ProtocolElements; -import io.openvidu.client.internal.RoomClosedInfo; -import io.openvidu.client.internal.SendMessageInfo; - -/** - * Service that handles server JSON-RPC events. - * - * @author Radu Tom Vlad - */ -public class ServerJsonRpcHandler extends DefaultJsonRpcHandler { - - private static final Logger log = LoggerFactory.getLogger(ServerJsonRpcHandler.class); - - private BlockingQueue notifications = new ArrayBlockingQueue(100); - - @Override - public void handleRequest(Transaction transaction, Request request) throws Exception { - Notification notif = null; - try { - switch (request.getMethod()) { - case ProtocolElements.ICECANDIDATE_METHOD: - notif = iceCandidate(transaction, request); - break; - case ProtocolElements.MEDIAERROR_METHOD: - notif = mediaError(transaction, request); - break; - case ProtocolElements.PARTICIPANTJOINED_METHOD: - notif = participantJoined(transaction, request); - break; - case ProtocolElements.PARTICIPANTLEFT_METHOD: - notif = participantLeft(transaction, request); - break; - case ProtocolElements.PARTICIPANTEVICTED_METHOD: - notif = participantEvicted(transaction, request); - break; - case ProtocolElements.PARTICIPANTPUBLISHED_METHOD: - notif = participantPublished(transaction, request); - break; - case ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD: - notif = participantUnpublished(transaction, request); - break; - case ProtocolElements.ROOMCLOSED_METHOD: - notif = roomClosed(transaction, request); - break; - case ProtocolElements.PARTICIPANTSENDMESSAGE_METHOD: - notif = participantSendMessage(transaction, request); - break; - default: - throw new Exception("Unrecognized request " + request.getMethod()); - } - } catch (Exception e) { - log.error("Exception processing request {}", request, e); - transaction.sendError(e); - return; - } - if (notif != null) { - try { - notifications.put(notif); - log.debug("Enqueued notification {}", notif); - } catch (InterruptedException e) { - log.warn("Interrupted when enqueuing notification {}", notif, e); - } - } - } - - private Notification participantSendMessage(Transaction transaction, - Request request) { - String data = JsonRoomUtils.getRequestParam(request, - ProtocolElements.PARTICIPANTSENDMESSAGE_DATA_PARAM, String.class); - String from = JsonRoomUtils.getRequestParam(request, - ProtocolElements.PARTICIPANTSENDMESSAGE_FROM_PARAM, String.class); - String type = JsonRoomUtils.getRequestParam(request, - ProtocolElements.PARTICIPANTSENDMESSAGE_TYPE_PARAM, String.class); - SendMessageInfo eventInfo = new SendMessageInfo(data, from, type); - log.debug("Recvd send message event {}", eventInfo); - return eventInfo; - } - - private Notification roomClosed(Transaction transaction, Request request) { - String room = JsonRoomUtils.getRequestParam(request, ProtocolElements.ROOMCLOSED_ROOM_PARAM, - String.class); - RoomClosedInfo eventInfo = new RoomClosedInfo(room); - log.debug("Recvd room closed event {}", eventInfo); - return eventInfo; - } - - private Notification participantUnpublished(Transaction transaction, - Request request) { - String name = JsonRoomUtils.getRequestParam(request, - ProtocolElements.PARTICIPANTUNPUBLISHED_NAME_PARAM, String.class); - ParticipantUnpublishedInfo eventInfo = new ParticipantUnpublishedInfo(name); - log.debug("Recvd participant unpublished event {}", eventInfo); - return eventInfo; - } - - private Notification participantPublished(Transaction transaction, Request request) { - String id = JsonRoomUtils.getRequestParam(request, - ProtocolElements.PARTICIPANTPUBLISHED_USER_PARAM, String.class); - JsonArray jsonStreams = JsonRoomUtils.getRequestParam(request, - ProtocolElements.PARTICIPANTPUBLISHED_STREAMS_PARAM, JsonArray.class); - Iterator streamIt = jsonStreams.iterator(); - List streams = new ArrayList(); - while (streamIt.hasNext()) { - streams.add(JsonRoomUtils.getResponseProperty(streamIt.next(), - ProtocolElements.PARTICIPANTPUBLISHED_STREAMID_PARAM, String.class)); - } - ParticipantPublishedInfo eventInfo = new ParticipantPublishedInfo(id, streams); - log.debug("Recvd published event {}", eventInfo); - return eventInfo; - } - - private Notification participantEvicted(Transaction transaction, Request request) { - ParticipantEvictedInfo eventInfo = new ParticipantEvictedInfo(); - log.debug("Recvd participant evicted event {}", eventInfo); - return eventInfo; - } - - private Notification participantLeft(Transaction transaction, Request request) { - String name = JsonRoomUtils.getRequestParam(request, - ProtocolElements.PARTICIPANTLEFT_NAME_PARAM, String.class); - ParticipantLeftInfo eventInfo = new ParticipantLeftInfo(name); - log.debug("Recvd participant left event {}", eventInfo); - return eventInfo; - } - - private Notification participantJoined(Transaction transaction, Request request) { - String id = JsonRoomUtils.getRequestParam(request, - ProtocolElements.PARTICIPANTJOINED_USER_PARAM, String.class); - ParticipantJoinedInfo eventInfo = new ParticipantJoinedInfo(id); - log.debug("Recvd participant joined event {}", eventInfo); - return eventInfo; - } - - private Notification mediaError(Transaction transaction, Request request) { - String description = JsonRoomUtils.getRequestParam(request, - ProtocolElements.MEDIAERROR_ERROR_PARAM, String.class); - MediaErrorInfo eventInfo = new MediaErrorInfo(description); - log.debug("Recvd media error event {}", eventInfo); - return eventInfo; - } - - private Notification iceCandidate(Transaction transaction, Request request) { - - String candidate = JsonRoomUtils.getRequestParam(request, - ProtocolElements.ICECANDIDATE_CANDIDATE_PARAM, String.class); - String sdpMid = JsonRoomUtils.getRequestParam(request, - ProtocolElements.ICECANDIDATE_SDPMID_PARAM, String.class); - int sdpMLineIndex = JsonRoomUtils.getRequestParam(request, - ProtocolElements.ICECANDIDATE_SDPMLINEINDEX_PARAM, Integer.class); - - IceCandidate iceCandidate = new IceCandidate(candidate, sdpMid, sdpMLineIndex); - - String endpoint = JsonRoomUtils.getRequestParam(request, - ProtocolElements.ICECANDIDATE_EPNAME_PARAM, String.class); - - IceCandidateInfo eventInfo = new IceCandidateInfo(iceCandidate, endpoint); - log.debug("Recvd ICE candidate event {}", eventInfo); - - return eventInfo; - } - - /** - * Blocks until an element is available and then returns it by removing it from the queue. - * - * @return a {@link Notification} from the queue, null when interrupted - * @see BlockingQueue#take() - */ - public Notification getNotification() { - try { - Notification notif = notifications.take(); - log.debug("Dequeued notification {}", notif); - return notif; - } catch (InterruptedException e) { - log.info("Interrupted while polling notifications' queue"); - return null; - } - } -} diff --git a/openvidu-client/src/main/java/io/openvidu/client/internal/IceCandidate.java b/openvidu-client/src/main/java/io/openvidu/client/internal/IceCandidate.java deleted file mode 100644 index 42564b1d..00000000 --- a/openvidu-client/src/main/java/io/openvidu/client/internal/IceCandidate.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.openvidu.client.internal; - -public class IceCandidate { - - private String candidate; - private String sdpMid; - private int sdpMLineIndex; - - public IceCandidate(String candidate, String sdpMid, int sdpMLineIndex) { - super(); - this.candidate = candidate; - this.sdpMid = sdpMid; - this.sdpMLineIndex = sdpMLineIndex; - } - - public String getCandidate() { - return candidate; - } - - public String getSdpMid() { - return sdpMid; - } - - public int getSdpMLineIndex() { - return sdpMLineIndex; - } - - @Override - public String toString() { - return "IceCandidate [candidate=" + candidate + ", sdpMid=" + sdpMid + ", sdpMLineIndex=" - + sdpMLineIndex + "]"; - } -} diff --git a/openvidu-client/src/main/java/io/openvidu/client/internal/IceCandidateInfo.java b/openvidu-client/src/main/java/io/openvidu/client/internal/IceCandidateInfo.java deleted file mode 100644 index face215b..00000000 --- a/openvidu-client/src/main/java/io/openvidu/client/internal/IceCandidateInfo.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.openvidu.client.internal; - -/** - * @see Notification - * - * @author Radu Tom Vlad - */ -public class IceCandidateInfo extends Notification { - - private IceCandidate iceCandidate; - private String endpointName; - - public IceCandidateInfo(IceCandidate iceCandidate, String endpointName) { - super(ProtocolElements.ICECANDIDATE_METHOD); - this.iceCandidate = iceCandidate; - this.endpointName = endpointName; - } - - public IceCandidate getIceCandidate() { - return iceCandidate; - } - - public void setIceCandidate(IceCandidate iceCandidate) { - this.iceCandidate = iceCandidate; - } - - public String getEndpointName() { - return endpointName; - } - - public void setEndpointName(String endpointName) { - this.endpointName = endpointName; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("["); - if (getMethod() != null) { - builder.append("method=").append(getMethod()).append(", "); - } - if (endpointName != null) { - builder.append("endpointName=").append(endpointName).append(", "); - } - if (iceCandidate != null) { - builder.append("iceCandidate=[sdpMLineIndex= ").append(iceCandidate.getSdpMLineIndex()) - .append(", sdpMid=").append(iceCandidate.getSdpMid()).append(", candidate=") - .append(iceCandidate.getCandidate()).append("]"); - } - builder.append("]"); - return builder.toString(); - } - -} diff --git a/openvidu-client/src/main/java/io/openvidu/client/internal/JsonRoomUtils.java b/openvidu-client/src/main/java/io/openvidu/client/internal/JsonRoomUtils.java deleted file mode 100644 index 552ebc9e..00000000 --- a/openvidu-client/src/main/java/io/openvidu/client/internal/JsonRoomUtils.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.openvidu.client.internal; - -import org.kurento.jsonrpc.message.Request; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import io.openvidu.client.OpenViduException; -import io.openvidu.client.OpenViduException.Code; - -/** - * JSON tools for extracting info from request or response elements. - * - * @author Radu Tom Vlad - */ -public class JsonRoomUtils { - - public static T getRequestParam(Request request, String paramName, Class type) { - return getRequestParam(request, paramName, type, false); - } - - public static T getRequestParam(Request request, String paramName, Class type, - boolean allowNull) { - JsonObject params = request.getParams(); - if (params == null) { - if (!allowNull) { - throw new OpenViduException(Code.TRANSPORT_REQUEST_ERROR_CODE, - "Invalid request lacking parameter '" + paramName + "'"); - } else { - return null; - } - } - return getConverted(params.get(paramName), paramName, type, allowNull); - } - - public static T getResponseProperty(JsonElement result, String property, Class type) { - return getResponseProperty(result, property, type, false); - } - - public static T getResponseProperty(JsonElement result, String property, Class type, - boolean allowNull) { - if (!(result instanceof JsonObject)) { - throw new OpenViduException(Code.TRANSPORT_RESPONSE_ERROR_CODE, - "Invalid response format. The response '" + result + "' should be a Json object"); - } - return getConverted(result.getAsJsonObject().get(property), property, type, allowNull); - } - - public static JsonArray getResponseArray(JsonElement result) { - if (!result.isJsonArray()) { - throw new OpenViduException(Code.TRANSPORT_RESPONSE_ERROR_CODE, - "Invalid response format. The response '" + result + "' should be a Json array"); - } - return result.getAsJsonArray(); - } - - @SuppressWarnings("unchecked") - private static T getConverted(JsonElement paramValue, String property, Class type, - boolean allowNull) { - if (paramValue == null) { - if (allowNull) { - return null; - } else { - throw new OpenViduException(Code.TRANSPORT_ERROR_CODE, "Invalid method lacking parameter '" - + property + "'"); - } - } - - if (type == String.class) { - if (paramValue.isJsonPrimitive()) { - return (T) paramValue.getAsString(); - } - } - - if (type == Integer.class) { - if (paramValue.isJsonPrimitive()) { - return (T) Integer.valueOf(paramValue.getAsInt()); - } - } - - if (type == JsonArray.class) { - if (paramValue.isJsonArray()) { - return (T) paramValue.getAsJsonArray(); - } - } - - throw new OpenViduException(Code.TRANSPORT_ERROR_CODE, "Param '" + property + "' with value '" - + paramValue + "' is not a " + type.getName()); - } -} diff --git a/openvidu-client/src/main/java/io/openvidu/client/internal/MediaErrorInfo.java b/openvidu-client/src/main/java/io/openvidu/client/internal/MediaErrorInfo.java deleted file mode 100644 index 9993e1b9..00000000 --- a/openvidu-client/src/main/java/io/openvidu/client/internal/MediaErrorInfo.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.openvidu.client.internal; - -/** - * - * @author Radu Tom Vlad - * - * @see Notification - */ -public class MediaErrorInfo extends Notification { - - private String description; - - public MediaErrorInfo(String description) { - super(ProtocolElements.MEDIAERROR_METHOD); - this.description = description; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("["); - if (getMethod() != null) { - builder.append("method=").append(getMethod()).append(", "); - } - if (description != null) { - builder.append("description=").append(description); - } - builder.append("]"); - return builder.toString(); - } -} diff --git a/openvidu-client/src/main/java/io/openvidu/client/internal/Notification.java b/openvidu-client/src/main/java/io/openvidu/client/internal/Notification.java deleted file mode 100644 index 85e16a57..00000000 --- a/openvidu-client/src/main/java/io/openvidu/client/internal/Notification.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.openvidu.client.internal; - -/** - * Wrapper for server events. - * - * @author Radu Tom Vlad - */ -public abstract class Notification { - - public enum Method { - ICECANDIDATE_METHOD(ProtocolElements.ICECANDIDATE_METHOD), MEDIAERROR_METHOD( - ProtocolElements.MEDIAERROR_METHOD), PARTICIPANTJOINED_METHOD( - ProtocolElements.PARTICIPANTJOINED_METHOD), PARTICIPANTLEFT_METHOD( - ProtocolElements.PARTICIPANTLEFT_METHOD), PARTICIPANTEVICTED_METHOD( - ProtocolElements.PARTICIPANTEVICTED_METHOD), PARTICIPANTPUBLISHED_METHOD( - ProtocolElements.PARTICIPANTPUBLISHED_METHOD), PARTICIPANTUNPUBLISHED_METHOD( - ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD), ROOMCLOSED_METHOD( - ProtocolElements.ROOMCLOSED_METHOD), PARTICIPANTSENDMESSAGE_METHOD( - ProtocolElements.PARTICIPANTSENDMESSAGE_METHOD); - - private String methodValue; - - private Method(String val) { - this.methodValue = val; - } - - public String getMethodValue() { - return methodValue; - } - - public static Method getFromValue(String val) { - for (Method m : Method.values()) { - if (m.methodValue.equals(val)) { - return m; - } - } - return null; - } - - @Override - public String toString() { - return getMethodValue().toString(); - } - } - - private Method method; - - public Notification(Method method) { - this.setMethod(method); - } - - public Notification(String methodValue) { - this(Method.getFromValue(methodValue)); - } - - public Method getMethod() { - return method; - } - - public void setMethod(Method method) { - this.method = method; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("["); - if (method != null) { - builder.append("method=").append(method); - } - builder.append("]"); - return builder.toString(); - } -} diff --git a/openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantEvictedInfo.java b/openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantEvictedInfo.java deleted file mode 100644 index 5c3562b4..00000000 --- a/openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantEvictedInfo.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.openvidu.client.internal; - -/** - * @see Notification - * - * @author Radu Tom Vlad - */ -public class ParticipantEvictedInfo extends Notification { - - public ParticipantEvictedInfo() { - super(ProtocolElements.PARTICIPANTEVICTED_METHOD); - } - -} diff --git a/openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantJoinedInfo.java b/openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantJoinedInfo.java deleted file mode 100644 index ee8b0c01..00000000 --- a/openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantJoinedInfo.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.openvidu.client.internal; - -/** - * @see Notification - * - * @author Radu Tom Vlad - */ -public class ParticipantJoinedInfo extends Notification { - - private String id; - - public ParticipantJoinedInfo(String id) { - super(ProtocolElements.PARTICIPANTJOINED_METHOD); - this.id = id; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("["); - if (getMethod() != null) { - builder.append("method=").append(getMethod()).append(", "); - } - if (id != null) { - builder.append("id=").append(id); - } - builder.append("]"); - return builder.toString(); - } -} diff --git a/openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantLeftInfo.java b/openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantLeftInfo.java deleted file mode 100644 index 10217338..00000000 --- a/openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantLeftInfo.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.openvidu.client.internal; - -/** - * @see Notification - * - * @author Radu Tom Vlad - */ -public class ParticipantLeftInfo extends Notification { - - private String name; - - public ParticipantLeftInfo(String name) { - super(ProtocolElements.PARTICIPANTLEFT_METHOD); - this.name = name; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("["); - if (getMethod() != null) { - builder.append("method=").append(getMethod()).append(", "); - } - if (name != null) { - builder.append("name=").append(name); - } - builder.append("]"); - return builder.toString(); - } -} diff --git a/openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantPublishedInfo.java b/openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantPublishedInfo.java deleted file mode 100644 index ab960896..00000000 --- a/openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantPublishedInfo.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.openvidu.client.internal; - -import java.util.List; - -/** - * @see Notification - * - * @author Radu Tom Vlad - */ -public class ParticipantPublishedInfo extends Notification { - - private String id; - private List streams; - - public ParticipantPublishedInfo(String id, List streams) { - super(ProtocolElements.PARTICIPANTPUBLISHED_METHOD); - this.id = id; - this.streams = streams; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public List getStreams() { - return streams; - } - - public void setStreams(List streams) { - this.streams = streams; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("["); - if (getMethod() != null) { - builder.append("method=").append(getMethod()).append(", "); - } - if (id != null) { - builder.append("id=").append(id).append(", "); - } - if (streams != null) { - builder.append("streams=").append(streams); - } - builder.append("]"); - return builder.toString(); - } -} diff --git a/openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantUnpublishedInfo.java b/openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantUnpublishedInfo.java deleted file mode 100644 index ae2e4e62..00000000 --- a/openvidu-client/src/main/java/io/openvidu/client/internal/ParticipantUnpublishedInfo.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.openvidu.client.internal; - -/** - * @see Notification - * - * @author Radu Tom Vlad - */ -public class ParticipantUnpublishedInfo extends Notification { - - private String name; - - public ParticipantUnpublishedInfo(String name) { - super(ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD); - this.name = name; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("["); - if (getMethod() != null) { - builder.append("method=").append(getMethod()).append(", "); - } - if (name != null) { - builder.append("name=").append(name); - } - builder.append("]"); - return builder.toString(); - } -} diff --git a/openvidu-client/src/main/java/io/openvidu/client/internal/ProtocolElements.java b/openvidu-client/src/main/java/io/openvidu/client/internal/ProtocolElements.java deleted file mode 100644 index a5e2ea99..00000000 --- a/openvidu-client/src/main/java/io/openvidu/client/internal/ProtocolElements.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.openvidu.client.internal; - -/** - * This class defines constant values of client-server messages and their - * parameters. - * - * @author Radu Tom Vlad - */ -public class ProtocolElements { - - // ---------------------------- CLIENT REQUESTS ----------------------- - - public static final String SENDMESSAGE_ROOM_METHOD = "sendMessage"; - public static final String SENDMESSAGE_MESSAGE_PARAM = "message"; - - public static final String LEAVEROOM_METHOD = "leaveRoom"; - - public static final String JOINROOM_METHOD = "joinRoom"; - public static final String JOINROOM_USER_PARAM = "user"; - public static final String JOINROOM_TOKEN_PARAM = "token"; - public static final String JOINROOM_ROOM_PARAM = "session"; - public static final String JOINROOM_METADATA_PARAM = "metadata"; - public static final String JOINROOM_SECRET_PARAM = "secret"; - public static final String JOINROOM_PLATFORM_PARAM = "platform"; - public static final String JOINROOM_SDKVERSION_PARAM = "sdkVersion"; - public static final String JOINROOM_RECORDER_PARAM = "recorder"; - public static final String JOINROOM_STT_PARAM = "stt"; - - public static final String JOINROOM_PEERID_PARAM = "id"; - public static final String JOINROOM_PEERCREATEDAT_PARAM = "createdAt"; - public static final String JOINROOM_PEERSTREAMS_PARAM = "streams"; - public static final String JOINROOM_PEERSTREAMID_PARAM = "id"; - public static final String JOINROOM_PEERSTREAMHASAUDIO_PARAM = "hasAudio"; - public static final String JOINROOM_PEERSTREAMHASVIDEO_PARAM = "hasVideo"; - public static final String JOINROOM_PEERSTREAMAUDIOACTIVE_PARAM = "audioActive"; - public static final String JOINROOM_PEERSTREAMVIDEOACTIVE_PARAM = "videoActive"; - public static final String JOINROOM_PEERSTREAMTYPEOFVIDEO_PARAM = "typeOfVideo"; - public static final String JOINROOM_PEERSTREAMFRAMERATE_PARAM = "frameRate"; - public static final String JOINROOM_PEERSTREAMVIDEODIMENSIONS_PARAM = "videoDimensions"; - public static final String JOINROOM_PEERSTREAMFILTER_PARAM = "filter"; - - public static final String PUBLISHVIDEO_METHOD = "publishVideo"; - public static final String PUBLISHVIDEO_SDPOFFER_PARAM = "sdpOffer"; - public static final String PUBLISHVIDEO_DOLOOPBACK_PARAM = "doLoopback"; - public static final String PUBLISHVIDEO_SDPANSWER_PARAM = "sdpAnswer"; - public static final String PUBLISHVIDEO_STREAMID_PARAM = "id"; - public static final String PUBLISHVIDEO_CREATEDAT_PARAM = "createdAt"; - public static final String PUBLISHVIDEO_HASAUDIO_PARAM = "hasAudio"; - public static final String PUBLISHVIDEO_HASVIDEO_PARAM = "hasVideo"; - public static final String PUBLISHVIDEO_AUDIOACTIVE_PARAM = "audioActive"; - public static final String PUBLISHVIDEO_VIDEOACTIVE_PARAM = "videoActive"; - public static final String PUBLISHVIDEO_TYPEOFVIDEO_PARAM = "typeOfVideo"; - public static final String PUBLISHVIDEO_FRAMERATE_PARAM = "frameRate"; - public static final String PUBLISHVIDEO_VIDEODIMENSIONS_PARAM = "videoDimensions"; - public static final String PUBLISHVIDEO_KURENTOFILTER_PARAM = "filter"; - - public static final String UNPUBLISHVIDEO_METHOD = "unpublishVideo"; - - public static final String PREPARERECEIVEVIDEO_METHOD = "prepareReceiveVideoFrom"; - public static final String PREPARERECEIVEVIDEO_SDPOFFER_PARAM = "sdpOffer"; - public static final String PREPARERECEIVEVIDEO_RECONNECT_PARAM = "reconnect"; - - public static final String RECEIVEVIDEO_METHOD = "receiveVideoFrom"; - public static final String RECEIVEVIDEO_SDPOFFER_PARAM = "sdpOffer"; - public static final String RECEIVEVIDEO_SENDER_PARAM = "sender"; - public static final String RECEIVEVIDEO_SDPANSWER_PARAM = "sdpAnswer"; - - public static final String UNSUBSCRIBEFROMVIDEO_METHOD = "unsubscribeFromVideo"; - public static final String UNSUBSCRIBEFROMVIDEO_SENDER_PARAM = "sender"; - - public static final String ONICECANDIDATE_METHOD = "onIceCandidate"; - public static final String ONICECANDIDATE_EPNAME_PARAM = "endpointName"; - public static final String ONICECANDIDATE_CANDIDATE_PARAM = "candidate"; - public static final String ONICECANDIDATE_SDPMIDPARAM = "sdpMid"; - public static final String ONICECANDIDATE_SDPMLINEINDEX_PARAM = "sdpMLineIndex"; - - public static final String CUSTOMREQUEST_METHOD = "customRequest"; - - public static final String STREAMPROPERTYCHANGED_METHOD = "streamPropertyChanged"; - public static final String STREAMPROPERTYCHANGED_CONNECTIONID_PARAM = "connectionId"; - public static final String STREAMPROPERTYCHANGED_STREAMID_PARAM = "streamId"; - public static final String STREAMPROPERTYCHANGED_PROPERTY_PARAM = "property"; - public static final String STREAMPROPERTYCHANGED_NEWVALUE_PARAM = "newValue"; - public static final String STREAMPROPERTYCHANGED_REASON_PARAM = "reason"; - - public static final String CONNECTIONPERTYCHANGED_METHOD = "connectionPropertyChanged"; - public static final String CONNECTIONROPERTYCHANGED_PROPERTY_PARAM = "property"; - public static final String CONNECTIONPROPERTYCHANGED_NEWVALUE_PARAM = "newValue"; - - public static final String NETWORKQUALITYLEVELCHANGED_METHOD = "networkQualityLevelChanged"; - public static final String NETWORKQUALITYCHANGED_CONNECTIONID_PARAM = "connectionId"; - public static final String NETWORKQUALITYCHANGED_NEWVALUE_PARAM = "newValue"; - public static final String NETWORKQUALITYCHANGED_OLDVALUE_PARAM = "oldValue"; - - public static final String FORCEDISCONNECT_METHOD = "forceDisconnect"; - public static final String FORCEDISCONNECT_CONNECTIONID_PARAM = "connectionId"; - - public static final String FORCEUNPUBLISH_METHOD = "forceUnpublish"; - public static final String FORCEUNPUBLISH_STREAMID_PARAM = "streamId"; - - public static final String APPLYFILTER_METHOD = "applyFilter"; - public static final String FILTER_STREAMID_PARAM = "streamId"; - public static final String FILTER_TYPE_PARAM = "type"; - public static final String FILTER_OPTIONS_PARAM = "options"; - public static final String FILTER_METHOD_PARAM = "method"; - public static final String FILTER_PARAMS_PARAM = "params"; - public static final String EXECFILTERMETHOD_METHOD = "execFilterMethod"; - public static final String EXECFILTERMETHOD_LASTEXECMETHOD_PARAM = "lastExecMethod"; - public static final String REMOVEFILTER_METHOD = "removeFilter"; - public static final String ADDFILTEREVENTLISTENER_METHOD = "addFilterEventListener"; - public static final String REMOVEFILTEREVENTLISTENER_METHOD = "removeFilterEventListener"; - - public static final String FILTEREVENTDISPATCHED_METHOD = "filterEventDispatched"; - public static final String FILTEREVENTLISTENER_CONNECTIONID_PARAM = "connectionId"; - public static final String FILTEREVENTLISTENER_STREAMID_PARAM = "streamId"; - public static final String FILTEREVENTLISTENER_FILTERTYPE_PARAM = "filterType"; - public static final String FILTEREVENTLISTENER_EVENTTYPE_PARAM = "eventType"; - public static final String FILTEREVENTLISTENER_DATA_PARAM = "data"; - - public static final String RECONNECTSTREAM_METHOD = "reconnectStream"; - public static final String RECONNECTSTREAM_STREAM_PARAM = "stream"; - public static final String RECONNECTSTREAM_SDPSTRING_PARAM = "sdpString"; - public static final String RECONNECTSTREAM_FORCIBLYRECONNECT_PARAM = "forciblyReconnect"; - public static final String RECONNECTSTREAM_SDPOFFER_PARAM = "sdpOffer"; - - public static final String VIDEODATA_METHOD = "videoData"; - - public static final String ECHO_METHOD = "echo"; - - public static final String FORCIBLYRECONNECTSUBSCRIBER_METHOD = "forciblyReconnectSubscriber"; - public static final String FORCIBLYRECONNECTSUBSCRIBER_CONNECTIONID_PARAM = "connectionId"; - public static final String FORCIBLYRECONNECTSUBSCRIBER_STREAMID_PARAM = "streamId"; - public static final String FORCIBLYRECONNECTSUBSCRIBER_SDPOFFER_PARAM = "sdpOffer"; - - public static final String SUBSCRIBETOSPEECHTOTEXT_METHOD = "subscribeToSpeechToText"; - public static final String SUBSCRIBETOSPEECHTOTEXT_CONNECTIONID_PARAM = "connectionId"; - public static final String SUBSCRIBETOSPEECHTOTEXT_LANG_PARAM = "lang"; - - public static final String UNSUBSCRIBEFROMSPEECHTOTEXT_METHOD = "unsubscribeFromSpeechToText"; - public static final String UNSUBSCRIBEFROMSPEECHTOTEXT_CONNECTIONID_PARAM = "connectionId"; - - // ---------------------------- SERVER RESPONSES & EVENTS ----------------- - - public static final String PARTICIPANTJOINED_METHOD = "participantJoined"; - public static final String PARTICIPANTJOINED_USER_PARAM = "id"; - public static final String PARTICIPANTJOINED_FINALUSERID_PARAM = "finalUserId"; - public static final String PARTICIPANTJOINED_CREATEDAT_PARAM = "createdAt"; - public static final String PARTICIPANTJOINED_METADATA_PARAM = "metadata"; - public static final String PARTICIPANTJOINED_VALUE_PARAM = "value"; - public static final String PARTICIPANTJOINED_SESSION_PARAM = "session"; - public static final String PARTICIPANTJOINED_VERSION_PARAM = "version"; - public static final String PARTICIPANTJOINED_MEDIASERVER_PARAM = "mediaServer"; - public static final String PARTICIPANTJOINED_SIMULCAST_PARAM = "videoSimulcast"; - public static final String PARTICIPANTJOINED_RECORD_PARAM = "record"; - public static final String PARTICIPANTJOINED_ROLE_PARAM = "role"; - public static final String PARTICIPANTJOINED_COTURNIP_PARAM = "coturnIp"; - public static final String PARTICIPANTJOINED_COTURNPORT_PARAM = "coturnPort"; - public static final String PARTICIPANTJOINED_CUSTOM_ICE_SERVERS = "customIceServers"; - public static final String PARTICIPANTJOINED_TURNUSERNAME_PARAM = "turnUsername"; - public static final String PARTICIPANTJOINED_TURNCREDENTIAL_PARAM = "turnCredential"; - public static final String PARTICIPANTJOINED_RECORDINGID_PARAM = "recordingId"; - public static final String PARTICIPANTJOINED_RECORDINGNAME_PARAM = "recordingName"; - - public static final String PARTICIPANTLEFT_METHOD = "participantLeft"; - public static final String PARTICIPANTLEFT_NAME_PARAM = "connectionId"; - public static final String PARTICIPANTLEFT_REASON_PARAM = "reason"; - - public static final String PARTICIPANTEVICTED_METHOD = "participantEvicted"; - public static final String PARTICIPANTEVICTED_CONNECTIONID_PARAM = "connectionId"; - public static final String PARTICIPANTEVICTED_REASON_PARAM = "reason"; - - public static final String PARTICIPANTPUBLISHED_METHOD = "participantPublished"; - public static final String PARTICIPANTPUBLISHED_USER_PARAM = "id"; - public static final String PARTICIPANTPUBLISHED_STREAMS_PARAM = "streams"; - public static final String PARTICIPANTPUBLISHED_STREAMID_PARAM = "id"; - public static final String PARTICIPANTPUBLISHED_CREATEDAT_PARAM = "createdAt"; - public static final String PARTICIPANTPUBLISHED_HASAUDIO_PARAM = "hasAudio"; - public static final String PARTICIPANTPUBLISHED_HASVIDEO_PARAM = "hasVideo"; - public static final String PARTICIPANTPUBLISHED_AUDIOACTIVE_PARAM = "audioActive"; - public static final String PARTICIPANTPUBLISHED_VIDEOACTIVE_PARAM = "videoActive"; - public static final String PARTICIPANTPUBLISHED_TYPEOFVIDEO_PARAM = "typeOfVideo"; - public static final String PARTICIPANTPUBLISHED_FRAMERATE_PARAM = "frameRate"; - public static final String PARTICIPANTPUBLISHED_VIDEODIMENSIONS_PARAM = "videoDimensions"; - public static final String PARTICIPANTPUBLISHED_FILTER_PARAM = "filter"; - - public static final String PARTICIPANTUNPUBLISHED_METHOD = "participantUnpublished"; - public static final String PARTICIPANTUNPUBLISHED_NAME_PARAM = "connectionId"; - public static final String PARTICIPANTUNPUBLISHED_REASON_PARAM = "reason"; - - public static final String PARTICIPANTSENDMESSAGE_METHOD = "sendMessage"; - public static final String PARTICIPANTSENDMESSAGE_DATA_PARAM = "data"; - public static final String PARTICIPANTSENDMESSAGE_FROM_PARAM = "from"; - public static final String PARTICIPANTSENDMESSAGE_TYPE_PARAM = "type"; - - public static final String ROOMCLOSED_METHOD = "roomClosed"; - public static final String ROOMCLOSED_ROOM_PARAM = "sessionId"; - - public static final String MEDIAERROR_METHOD = "mediaError"; - public static final String MEDIAERROR_ERROR_PARAM = "error"; - - public static final String ICECANDIDATE_METHOD = "iceCandidate"; - public static final String ICECANDIDATE_SENDERCONNECTIONID_PARAM = "senderConnectionId"; - public static final String ICECANDIDATE_EPNAME_PARAM = "endpointName"; - public static final String ICECANDIDATE_CANDIDATE_PARAM = "candidate"; - public static final String ICECANDIDATE_SDPMID_PARAM = "sdpMid"; - public static final String ICECANDIDATE_SDPMLINEINDEX_PARAM = "sdpMLineIndex"; - - public static final String RECORDINGSTARTED_METHOD = "recordingStarted"; - public static final String RECORDINGSTARTED_ID_PARAM = "id"; - public static final String RECORDINGSTARTED_NAME_PARAM = "name"; - public static final String RECORDINGSTOPPED_REASON_PARAM = "reason"; - - public static final String RECORDINGSTOPPED_METHOD = "recordingStopped"; - public static final String RECORDINGSTOPPED_ID_PARAM = "id"; - - public static final String BROADCASTSTARTED_METHOD = "broadcastStarted"; - - public static final String BROADCASTSTOPPED_METHOD = "broadcastStopped"; - - public static final String SPEECHTOTEXTMESSAGE_METHOD = "speechToTextMessage"; - public static final String SPEECHTOTEXTMESSAGE_TIMESTAMP_PARAM = "timestamp"; - public static final String SPEECHTOTEXTMESSAGE_SESSIONID_PARAM = "sessionId"; - public static final String SPEECHTOTEXTMESSAGE_CONNECTIONID_PARAM = "connectionId"; - public static final String SPEECHTOTEXTMESSAGE_TEXT_PARAM = "text"; - public static final String SPEECHTOTEXTMESSAGE_REASON_PARAM = "reason"; - public static final String SPEECHTOTEXTMESSAGE_RAW_PARAM = "raw"; - public static final String SPEECHTOTEXTMESSAGE_LANG_PARAM = "lang"; - - public static final String SPEECHTOTEXTDISCONNECTED_METHOD = "speechToTextDisconnected"; - public static final String SPEECHTOTEXTDISCONNECTED_MESSAGE_PARAM = "message"; - - public static final String CUSTOM_NOTIFICATION = "customNotification"; - - public static final String RECORDER_PARTICIPANT_PUBLICID = "RECORDER"; - public static final String STT_PARTICIPANT_PUBLICID = "SPEECH_TO_TEXT"; -} diff --git a/openvidu-client/src/main/java/io/openvidu/client/internal/RoomClosedInfo.java b/openvidu-client/src/main/java/io/openvidu/client/internal/RoomClosedInfo.java deleted file mode 100644 index b29c1ae4..00000000 --- a/openvidu-client/src/main/java/io/openvidu/client/internal/RoomClosedInfo.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.openvidu.client.internal; - -/** - * @see Notification - * - * @author Radu Tom Vlad - */ -public class RoomClosedInfo extends Notification { - - private String room; - - public RoomClosedInfo(String room) { - super(ProtocolElements.ROOMCLOSED_METHOD); - this.room = room; - } - - public String getRoom() { - return room; - } - - public void setRoom(String room) { - this.room = room; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("["); - if (getMethod() != null) { - builder.append("method=").append(getMethod()).append(", "); - } - if (room != null) { - builder.append("room=").append(room); - } - builder.append("]"); - return builder.toString(); - } -} diff --git a/openvidu-client/src/main/java/io/openvidu/client/internal/SendMessageInfo.java b/openvidu-client/src/main/java/io/openvidu/client/internal/SendMessageInfo.java deleted file mode 100644 index 21f3c651..00000000 --- a/openvidu-client/src/main/java/io/openvidu/client/internal/SendMessageInfo.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.openvidu.client.internal; - -/** - * @see Notification - * - * @author Radu Tom Vlad - */ -public class SendMessageInfo extends Notification { - - private String room; - private String user; - private String message; - - public SendMessageInfo(String room, String user, String message) { - super(ProtocolElements.PARTICIPANTSENDMESSAGE_METHOD); - this.room = room; - this.user = user; - this.message = message; - } - - public String getRoom() { - return room; - } - - public void setRoom(String room) { - this.room = room; - } - - public String getUser() { - return user; - } - - public void setUser(String user) { - this.user = user; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("["); - if (getMethod() != null) { - builder.append("method=").append(getMethod()).append(", "); - } - if (room != null) { - builder.append("room=").append(room).append(", "); - } - if (user != null) { - builder.append("user=").append(user).append(", "); - } - if (message != null) { - builder.append("message=").append(message); - } - builder.append("]"); - return builder.toString(); - } -} diff --git a/openvidu-client/src/test/java/io/openvidu/client/test/OpenViduClientTest.java b/openvidu-client/src/test/java/io/openvidu/client/test/OpenViduClientTest.java deleted file mode 100644 index 2bee1201..00000000 --- a/openvidu-client/src/test/java/io/openvidu/client/test/OpenViduClientTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * (C) Copyright 2017-2022 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.openvidu.client.test; - -import static io.openvidu.client.internal.ProtocolElements.JOINROOM_METHOD; -import static io.openvidu.client.internal.ProtocolElements.JOINROOM_ROOM_PARAM; -import static io.openvidu.client.internal.ProtocolElements.JOINROOM_USER_PARAM; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.kurento.jsonrpc.client.JsonRpcClient; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; - -import io.openvidu.client.OpenViduClient; -import io.openvidu.client.ServerJsonRpcHandler; - -/** - * Unit tests for the room client protocol. - * - * @author Radu Tom Vlad (rvlad@naevatec.com) - * @since 6.3.1 - */ -public class OpenViduClientTest { - - private static OpenViduClient client; - private static ServerJsonRpcHandler serverHandler; - private static JsonRpcClient jsonRpcClient; - - @BeforeAll - public static void setup() { - jsonRpcClient = mock(JsonRpcClient.class); - serverHandler = new ServerJsonRpcHandler(); - client = new OpenViduClient(jsonRpcClient, serverHandler); - } - - @Test - public void testRoomJoin() throws IOException { - JsonObject params = new JsonObject(); - params.addProperty(JOINROOM_ROOM_PARAM, "room"); - params.addProperty(JOINROOM_USER_PARAM, "user"); - - JsonObject result = new JsonObject(); - JsonArray value = new JsonArray(); - result.add("value", value); - - Map> joinResult = new HashMap>(); - - when(jsonRpcClient.sendRequest(JOINROOM_METHOD, params)).thenReturn(result); - Assertions.assertEquals(client.joinRoom("room", "user"), joinResult); - } -} diff --git a/openvidu-components-angular/.prettierignore b/openvidu-components-angular/.prettierignore new file mode 100644 index 00000000..7adb7688 --- /dev/null +++ b/openvidu-components-angular/.prettierignore @@ -0,0 +1,5 @@ +build +node_modules +.github +dist +docs diff --git a/openvidu-components-angular/README.md b/openvidu-components-angular/README.md index ae7902fb..7a41bede 100644 --- a/openvidu-components-angular/README.md +++ b/openvidu-components-angular/README.md @@ -10,21 +10,21 @@ openvidu-components-angular │ └───projects │ - └─── openvidu-angular-v2compatibility + └─── openvidu-components-angular ``` ## How to develop with ease: Run `ng serve` for a dev server. -Run, in a new terminal, `npm run lib:serve` for serving the openvidu-angular-v2compatibility library with live reload for listening changes +Run, in a new terminal, `npm run lib:serve` for serving the openvidu-components-angular library with live reload for listening changes ## Code scaffolding -For generate new components in openvidu-angular-v2compatibility: +For generate new components in openvidu-components-angular: ```bash -ng g component components/component-name --project=openvidu-angular-v2compatibility +ng g component components/component-name --project=openvidu-components-angular ``` diff --git a/openvidu-components-angular/angular.json b/openvidu-components-angular/angular.json index 03d9e59e..29a1c314 100644 --- a/openvidu-components-angular/angular.json +++ b/openvidu-components-angular/angular.json @@ -15,17 +15,19 @@ "prefix": "app", "architect": { "build": { - "builder": "@angular-devkit/build-angular:browser", + "builder": "@angular-devkit/build-angular:application", "options": { - "outputPath": "dist/openvidu-components-testapp", + "outputPath": { + "base": "dist/openvidu-components-testapp" + }, "index": "src/index.html", - "main": "src/main.ts", "polyfills": ["zone.js"], "tsConfig": "tsconfig.app.json", "aot": true, "assets": ["src/favicon.ico", "src/assets"], "styles": ["src/styles.scss"], - "scripts": [] + "scripts": [], + "browser": "src/main.ts" }, "configurations": { "development": { @@ -33,9 +35,7 @@ "outputHashing": "all", "sourceMap": true, "namedChunks": false, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": false + "extractLicenses": true }, "production": { "fileReplacements": [ @@ -49,8 +49,6 @@ "sourceMap": false, "namedChunks": false, "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true, "budgets": [ { "type": "initial", @@ -69,7 +67,7 @@ "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { - "proxyConfig": "src/proxy.config.json", + "proxyConfig": "src/proxy.conf.json", "buildTarget": "openvidu-components-testapp:build" }, "configurations": { @@ -120,23 +118,23 @@ } } }, - "openvidu-angular": { + "openvidu-components-angular": { "projectType": "library", - "root": "projects/openvidu-angular", - "sourceRoot": "projects/openvidu-angular/src", + "root": "projects/openvidu-components-angular", + "sourceRoot": "projects/openvidu-components-angular/src", "prefix": "ov", "architect": { "build": { "builder": "@angular-devkit/build-angular:ng-packagr", "options": { - "project": "projects/openvidu-angular/ng-package.json" + "project": "projects/openvidu-components-angular/ng-package.json" }, "configurations": { "production": { - "tsConfig": "projects/openvidu-angular/tsconfig.lib.prod.json" + "tsConfig": "projects/openvidu-components-angular/tsconfig.lib.prod.json" }, "development": { - "tsConfig": "projects/openvidu-angular/tsconfig.lib.json" + "tsConfig": "projects/openvidu-components-angular/tsconfig.lib.json" } }, "defaultConfiguration": "production" @@ -144,9 +142,9 @@ "test": { "builder": "@angular-devkit/build-angular:karma", "options": { - "main": "projects/openvidu-angular/src/test.ts", - "tsConfig": "projects/openvidu-angular/tsconfig.spec.json", - "karmaConfig": "projects/openvidu-angular/karma.conf.js" + "main": "projects/openvidu-components-angular/src/test.ts", + "tsConfig": "projects/openvidu-components-angular/tsconfig.spec.json", + "karmaConfig": "projects/openvidu-components-angular/karma.conf.js" } } } @@ -188,7 +186,32 @@ { "type": "initial", "maximumWarning": "1mb", - "maximumError": "3mb" + "maximumError": "2mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ] + }, + "testing": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.testing.ts" + } + ], + "optimization": true, + "outputHashing": "none", + "sourceMap": false, + "namedChunks": false, + "extractLicenses": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "1mb", + "maximumError": "2mb" }, { "type": "anyComponentStyle", diff --git a/openvidu-components-angular/docs.md b/openvidu-components-angular/docs.md deleted file mode 100644 index a65d0f26..00000000 --- a/openvidu-components-angular/docs.md +++ /dev/null @@ -1,5 +0,0 @@ - * https://docs.google.com/document/d/1IUwdlxyCp3j88hBuf-2JOgp_DZOTDoZ_36ifR6JeKjg/edit#heading=h.hgzr1bwt9mj5 - * https://docs.google.com/document/d/1C4q9T1VNL26Am-14uqt19zrovQ6QdIYD-JQntOV2eHs/edit#heading=h.hgzr1bwt9mj5 - * https://docs.google.com/document/d/1yXD2Sqz4qbH1Q0pHMOmpKymsEYvSKNkyzy5MZl-7Mkc/edit#heading=h.amfwrsbwplqk - * https://docs.google.com/document/d/1atuocV-Skv5ymU7BsEPhXQk9INmEyqfFkMQ7C2Sg8RA/edit#heading=h.20jqupqw4tve - * https://docs.google.com/document/d/1ugQR_HA0JBVomjm-v3cOhEocxsCkcBtthUQYkdcI-Jk/edit \ No newline at end of file diff --git a/openvidu-components-angular/e2e/angular.test.ts b/openvidu-components-angular/e2e/angular.test.ts index bc55343d..7eadc60f 100644 --- a/openvidu-components-angular/e2e/angular.test.ts +++ b/openvidu-components-angular/e2e/angular.test.ts @@ -843,12 +843,12 @@ describe('Testing ATTRIBUTE DIRECTIVES', () => { expect(await utils.isPresent('branding-logo')).to.be.false; }); - it('should HIDE the DISPLAY SESSION name', async () => { + it('should HIDE the DISPLAY ROOM name', async () => { await browser.get(`${url}`); await utils.clickOn('#ovToolbar-checkbox'); - await utils.clickOn('#displaySessionName-checkbox'); + await utils.clickOn('#displayRoomName-checkbox'); await utils.clickOn('#apply-btn'); @@ -873,7 +873,7 @@ describe('Testing ATTRIBUTE DIRECTIVES', () => { await browser.sleep(500); - await utils.waitForElement('.mat-mdc-menu-content'); + await utils.waitForElement('#more-options-menu'); // Checking if fullscreen button is not present expect(await utils.isPresent('#fullscreen-btn')).to.be.false; @@ -894,7 +894,7 @@ describe('Testing ATTRIBUTE DIRECTIVES', () => { await utils.clickOn('#more-options-btn'); await browser.sleep(500); - await utils.waitForElement('.mat-mdc-menu-content'); + await utils.waitForElement('#more-options-menu'); // Checking if fullscreen button is not present expect(await utils.isPresent('#broadcasting-btn')).to.be.false; @@ -955,7 +955,7 @@ describe('Testing ATTRIBUTE DIRECTIVES', () => { await utils.waitForElement('#session-container'); await utils.waitForElement('#custom-stream'); - expect(await utils.isPresent('nickname-container')).to.be.false; + expect(await utils.isPresent('participant-name-container')).to.be.false; }); it('should HIDE the SETTINGS button', async () => { @@ -1181,7 +1181,7 @@ describe('Testing EVENTS', () => { await browser.sleep(500); - await utils.waitForElement('.mat-mdc-menu-content'); + await utils.waitForElement('#more-options-menu'); await utils.clickOn('#fullscreen-btn'); diff --git a/openvidu-components-angular/e2e/config.ts b/openvidu-components-angular/e2e/config.ts index 86ab2afe..612e0948 100644 --- a/openvidu-components-angular/e2e/config.ts +++ b/openvidu-components-angular/e2e/config.ts @@ -1,3 +1,3 @@ export const LAUNCH_MODE = process.env.LAUNCH_MODE || 'DEV'; -export const OPENVIDU_SERVER_URL = process.env.OPENVIDU_SERVER_URL || 'http://localhost:4443'; -export const OPENVIDU_SECRET = process.env.OPENVIDU_SECRET || 'MY_SECRET'; +export const OPENVIDU_CALL_SERVER = process.env.OPENVIDU_CALL_SERVER || 'http://localhost:5000'; + diff --git a/openvidu-components-angular/e2e/selenium.conf.ts b/openvidu-components-angular/e2e/selenium.conf.ts index 8dc8612b..462da367 100644 --- a/openvidu-components-angular/e2e/selenium.conf.ts +++ b/openvidu-components-angular/e2e/selenium.conf.ts @@ -10,9 +10,15 @@ interface BrowserConfig { browserName: string; } -const chromeArguments = ['--window-size=1280,1024', '--use-fake-ui-for-media-stream', '--use-fake-device-for-media-stream']; +const chromeArguments = [ + '--window-size=1300,1000', + '--headless', + '--use-fake-ui-for-media-stream', + '--use-fake-device-for-media-stream', + '--use-file-for-fake-audio-capture=e2e/assets/audio.wav' +]; const chromeArgumentsCI = [ - '--window-size=1280,1024', + '--window-size=1300,1000', '--headless', '--no-sandbox', '--disable-gpu', @@ -25,9 +31,9 @@ const chromeArgumentsCI = [ '--use-fake-ui-for-media-stream', '--use-fake-device-for-media-stream' ]; -const chromeArgumentsWithoutMediaDevices = ['--window-size=1024,768', '--deny-permission-prompts']; +const chromeArgumentsWithoutMediaDevices = ['--headless', '--window-size=1300,900', '--deny-permission-prompts']; const chromeArgumentsWithoutMediaDevicesCI = [ - '--window-size=1024,768', + '--window-size=1300,900', '--headless', '--no-sandbox', '--disable-gpu', diff --git a/openvidu-components-angular/e2e/utils.po.test.ts b/openvidu-components-angular/e2e/utils.po.test.ts index 85014ddb..3269b739 100644 --- a/openvidu-components-angular/e2e/utils.po.test.ts +++ b/openvidu-components-angular/e2e/utils.po.test.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { By, until, WebDriver, WebElement } from 'selenium-webdriver'; +import { By, Origin, until, WebDriver, WebElement } from 'selenium-webdriver'; export class OpenViduComponentsPO { private TIMEOUT = 10 * 1000; @@ -24,6 +24,10 @@ export class OpenViduComponentsPO { const elements = await this.browser.findElements(By.css(selector)); return elements.length > 0; } + async sendKeys(selector: string, keys: string): Promise { + const element = await this.waitForElement(selector); + await element.sendKeys(keys); + } async checkPrejoinIsPresent(): Promise { await this.waitForElement('#prejoin-container'); @@ -71,4 +75,81 @@ export class OpenViduComponentsPO { const element = await this.waitForElement(selector); await element.click(); } + + async hoverOn(selector: string): Promise { + const element = await this.waitForElement(selector); + const action = this.browser.actions().move({ origin: element, duration: 1000 }); + return action.perform(); + } + + async openTab(url: string): Promise { + const newTabScript = `window.open("${url}")`; + await this.browser.executeScript(newTabScript); + return this.browser.getAllWindowHandles(); + } + + async subscribeToDropEvent(): Promise { + const script = ` + document.dispatchEvent(new Event("webcomponentTestingEndedDragAndDropEvent")); + `; + await this.browser.executeScript(script); + } + + async dragToRight(x: number, y: number): Promise { + const script = ` + document.dispatchEvent(new CustomEvent("webcomponentTestingEndedDragAndDropRightEvent", {detail: {x: arguments[0], y: arguments[1]}})); + `; + await this.browser.executeScript(script, x, y); + } + + async toggleToolbarMoreOptions(): Promise { + await this.waitForElement('#more-options-btn'); + expect(await this.isPresent('#more-options-btn')).to.be.true; + await this.clickOn('#more-options-btn'); + await this.browser.sleep(500); + await this.waitForElement('#more-options-menu'); + } + + async toggleRecordingFromToolbar() { + // Open more options menu + await this.toggleToolbarMoreOptions(); + + await this.waitForElement('#recording-btn'); + expect(await this.isPresent('#recording-btn')).to.be.true; + await this.clickOn('#recording-btn'); + } + + async toggleFullscreenFromToolbar() { + // Open more options menu + await this.toggleToolbarMoreOptions(); + + const fullscreenButton = await this.waitForElement('#fullscreen-btn'); + expect(await this.isPresent('#fullscreen-btn')).to.be.true; + await fullscreenButton.click(); + } + + async togglePanel(panelName: string) { + switch (panelName) { + case 'activities': + await this.waitForElement('#activities-panel-btn'); + expect(await this.isPresent('#activities-panel-btn')).to.be.true; + await this.clickOn('#activities-panel-btn'); + break; + + case 'chat': + await this.waitForElement('#chat-panel-btn'); + await this.clickOn('#chat-panel-btn'); + break; + case 'participants': + await this.waitForElement('#participants-panel-btn'); + await this.clickOn('#participants-panel-btn'); + break; + + case 'settings': + await this.toggleToolbarMoreOptions(); + await this.waitForElement('#toolbar-settings-btn'); + await this.clickOn('#toolbar-settings-btn'); + break; + } + } } diff --git a/openvidu-components-angular/e2e/webcomponent-app/app.js b/openvidu-components-angular/e2e/webcomponent-app/app.js index 90df8eb1..946010af 100644 --- a/openvidu-components-angular/e2e/webcomponent-app/app.js +++ b/openvidu-components-angular/e2e/webcomponent-app/app.js @@ -6,8 +6,8 @@ var CAPTIONS_LANG; var CUSTOM_LANG_OPTIONS; var CUSTOM_CAPTIONS_LANG_OPTIONS; var PREJOIN; -var VIDEO_MUTED; -var AUDIO_MUTED; +var VIDEO_ENABLED; +var AUDIO_ENABLED; var SCREENSHARE_BUTTON; var FULLSCREEN_BUTTON; @@ -16,10 +16,10 @@ var RECORDING_BUTTON; var BROADCASTING_BUTTON; var CHAT_PANEL_BUTTON; var DISPLAY_LOGO; -var DISPLAY_SESSION_NAME; +var DISPLAY_ROOM_NAME; var DISPLAY_PARTICIPANT_NAME; var DISPLAY_AUDIO_DETECTION; -var SETTINGS_BUTTON; +var VIDEO_CONTROLS; var LEAVE_BUTTON; var PARTICIPANT_MUTE_BUTTON; var PARTICIPANTS_PANEL_BUTTON; @@ -30,37 +30,38 @@ var BROADCASTING_ERROR; var TOOLBAR_SETTINGS_BUTTON; var CAPTIONS_BUTTON; -var SINGLE_TOKEN; -var SESSION_NAME; +var ROOM_NAME; var FAKE_DEVICES; +var FAKE_RECORDINGS; var PARTICIPANT_NAME; -var OPENVIDU_SERVER_URL; -var OPENVIDU_SECRET; +var OPENVIDU_CALL_SERVER_URL; +// var OPENVIDU_SECRET; -$(document).ready(() => { +document.addEventListener('DOMContentLoaded', () => { var url = new URL(window.location.href); - OPENVIDU_SERVER_URL = url.searchParams.get('OV_URL'); - OPENVIDU_SECRET = url.searchParams.get('OV_SECRET'); - - SINGLE_TOKEN = url.searchParams.get('singleToken') === null ? false : url.searchParams.get('singleToken') === 'true'; + OPENVIDU_CALL_SERVER_URL = url.searchParams.get('OV_URL'); + // OPENVIDU_SECRET = url.searchParams.get('OV_SECRET'); FAKE_DEVICES = url.searchParams.get('fakeDevices') === null ? false : url.searchParams.get('fakeDevices') === 'true'; + FAKE_RECORDINGS = url.searchParams.get('fakeRecordings') === null ? false : url.searchParams.get('fakeRecordings') === 'true'; + // Directives MINIMAL = url.searchParams.get('minimal') === null ? false : url.searchParams.get('minimal') === 'true'; LANG = url.searchParams.get('lang') || 'en'; - CAPTIONS_LANG = url.searchParams.get('captionsLang') || 'en-US'; - CUSTOM_LANG_OPTIONS = - url.searchParams.get('langOptions') === null ? false : url.searchParams.get('langOptions') === 'true'; - CUSTOM_CAPTIONS_LANG_OPTIONS = - url.searchParams.get('captionsLangOptions') === null ? false : url.searchParams.get('captionsLangOptions') === 'true'; - PARTICIPANT_NAME = url.searchParams.get('participantName') || 'TEST_USER'; + CUSTOM_LANG_OPTIONS = url.searchParams.get('langOptions') === null ? false : url.searchParams.get('langOptions') === 'true'; + // CAPTIONS_LANG = url.searchParams.get('captionsLang') || 'en-US'; + // CUSTOM_CAPTIONS_LANG_OPTIONS = url.searchParams.get('captionsLangOptions') === null ? false : url.searchParams.get('captionsLangOptions') === 'true'; + PARTICIPANT_NAME = + url.searchParams.get('participantName') === null + ? 'TEST_USER' + Math.random().toString(36).substr(2, 9) + : url.searchParams.get('participantName'); PREJOIN = url.searchParams.get('prejoin') === null ? true : url.searchParams.get('prejoin') === 'true'; - VIDEO_MUTED = url.searchParams.get('videoMuted') === null ? false : url.searchParams.get('videoMuted') === 'true'; - AUDIO_MUTED = url.searchParams.get('audioMuted') === null ? false : url.searchParams.get('audioMuted') === 'true'; + VIDEO_ENABLED = url.searchParams.get('videoEnabled') === null ? true : url.searchParams.get('videoEnabled') === 'true'; + AUDIO_ENABLED = url.searchParams.get('audioEnabled') === null ? true : url.searchParams.get('audioEnabled') === 'true'; SCREENSHARE_BUTTON = url.searchParams.get('screenshareBtn') === null ? true : url.searchParams.get('screenshareBtn') === 'true'; RECORDING_BUTTON = url.searchParams.get('toolbarRecordingButton') === null ? true : url.searchParams.get('toolbarRecordingButton') === 'true'; @@ -95,53 +96,59 @@ $(document).ready(() => { } DISPLAY_LOGO = url.searchParams.get('displayLogo') === null ? true : url.searchParams.get('displayLogo') === 'true'; - DISPLAY_SESSION_NAME = - url.searchParams.get('displaySessionName') === null ? true : url.searchParams.get('displaySessionName') === 'true'; + DISPLAY_ROOM_NAME = url.searchParams.get('displayRoomName') === null ? true : url.searchParams.get('displayRoomName') === 'true'; DISPLAY_PARTICIPANT_NAME = url.searchParams.get('displayParticipantName') === null ? true : url.searchParams.get('displayParticipantName') === 'true'; DISPLAY_AUDIO_DETECTION = url.searchParams.get('displayAudioDetection') === null ? true : url.searchParams.get('displayAudioDetection') === 'true'; - SETTINGS_BUTTON = url.searchParams.get('settingsBtn') === null ? true : url.searchParams.get('settingsBtn') === 'true'; + VIDEO_CONTROLS = url.searchParams.get('videoControls') === null ? true : url.searchParams.get('videoControls') === 'true'; PARTICIPANT_MUTE_BUTTON = url.searchParams.get('participantMuteBtn') === null ? true : url.searchParams.get('participantMuteBtn') === 'true'; - SESSION_NAME = - url.searchParams.get('sessionName') === null ? `E2ESession${Math.floor(Date.now())}` : url.searchParams.get('sessionName'); + ROOM_NAME = url.searchParams.get('roomName') === null ? `E2ESession${Math.floor(Date.now())}` : url.searchParams.get('roomName'); var webComponent = document.querySelector('openvidu-webcomponent'); - webComponent.addEventListener('onJoinButtonClicked', (event) => appendElement('onJoinButtonClicked')); - webComponent.addEventListener('onToolbarLeaveButtonClicked', (event) => appendElement('onToolbarLeaveButtonClicked')); - webComponent.addEventListener('onToolbarCameraButtonClicked', (event) => appendElement('onToolbarCameraButtonClicked')); - webComponent.addEventListener('onToolbarMicrophoneButtonClicked', (event) => appendElement('onToolbarMicrophoneButtonClicked')); - webComponent.addEventListener('onToolbarScreenshareButtonClicked', (event) => appendElement('onToolbarScreenshareButtonClicked')); - webComponent.addEventListener('onToolbarParticipantsPanelButtonClicked', (event) => - appendElement('onToolbarParticipantsPanelButtonClicked') + webComponent.addEventListener('onTokenRequested', (event) => { + appendElement('onTokenRequested'); + console.log('Token ready', event.detail); + joinSession(ROOM_NAME, event.detail); + }); + webComponent.addEventListener('onReadyToJoin', (event) => appendElement('onReadyToJoin')); + webComponent.addEventListener('onRoomDisconnected', (event) => appendElement('onRoomDisconnected')); + webComponent.addEventListener('onVideoEnabledChanged', (event) => appendElement('onVideoEnabledChanged-' + event.detail)); + webComponent.addEventListener('onVideoDeviceChanged', (event) => appendElement('onVideoDeviceChanged')); + webComponent.addEventListener('onAudioEnabledChanged', (eSESSIONvent) => appendElement('onAudioEnabledChanged-' + event.detail)); + webComponent.addEventListener('onAudioDeviceChanged', (event) => appendElement('onAudioDeviceChanged')); + webComponent.addEventListener('onScreenShareEnabledChanged', (event) => appendElement('onScreenShareEnabledChanged')); + webComponent.addEventListener('onParticipantsPanelStatusChanged', (event) => + appendElement('onParticipantsPanelStatusChanged-' + event.detail.isOpened) ); - webComponent.addEventListener('onToolbarChatPanelButtonClicked', (event) => appendElement('onToolbarChatPanelButtonClicked')); - webComponent.addEventListener('onToolbarActivitiesPanelButtonClicked', (event) => - appendElement('onToolbarActivitiesPanelButtonClicked') + webComponent.addEventListener('onLangChanged', (event) => appendElement('onLangChanged-' + event.detail.lang)); + webComponent.addEventListener('onChatPanelStatusChanged', (event) => + appendElement('onChatPanelStatusChanged-' + event.detail.isOpened) ); - webComponent.addEventListener('onToolbarFullscreenButtonClicked', (event) => appendElement('onToolbarFullscreenButtonClicked')); + webComponent.addEventListener('onActivitiesPanelStatusChanged', (event) => + appendElement('onActivitiesPanelStatusChanged-' + event.detail.isOpened) + ); + webComponent.addEventListener('onSettingsPanelStatusChanged', (event) => + appendElement('onSettingsPanelStatusChanged-' + event.detail.isOpened) + ); + webComponent.addEventListener('onFullscreenEnabledChanged', (event) => appendElement('onFullscreenEnabledChanged-' + event.detail)); - webComponent.addEventListener('onToolbarStartRecordingClicked', async (event) => { - appendElement('onToolbarStartRecordingClicked'); + webComponent.addEventListener('onRecordingStartRequested', async (event) => { + appendElement('onRecordingStartRequested-' + event.detail.roomName); // Can't test the recording // RECORDING_ID = await startRecording(SESSION_NAME); }); // Can't test the recording - // webComponent.addEventListener('onToolbarStopRecordingClicked', async (event) => { - // appendElement('onToolbarStopRecordingClicked'); + // webComponent.addEventListener('onRecordingStopRequested', async (event) => { + // appendElement('onRecordingStopRequested-' + event.detail.roomName); // await stopRecording(RECORDING_ID); // }); - webComponent.addEventListener('onToolbarStopBroadcastingClicked', async (event) => { - appendElement('onToolbarStopBroadcastingClicked'); - }); - - webComponent.addEventListener('onActivitiesPanelStartRecordingClicked', async (event) => { - appendElement('onActivitiesPanelStartRecordingClicked'); - // RECORDING_ID = await startRecording(SESSION_NAME); + webComponent.addEventListener('onRecordingStopRequested', async (event) => { + appendElement('onRecordingStopRequested-' + event.detail.roomName); }); // Can't test the recording @@ -150,95 +157,77 @@ $(document).ready(() => { // await stopRecording(RECORDING_ID); // }); - webComponent.addEventListener('onActivitiesPanelDeleteRecordingClicked', (event) => { - appendElement('onActivitiesPanelDeleteRecordingClicked'); + webComponent.addEventListener('onRecordingDeleteRequested', (event) => { + const { roomName, recordingId } = event.detail; + appendElement(`onRecordingDeleteRequested-${roomName}-${recordingId}`); }); - webComponent.addEventListener('onActivitiesPanelStartBroadcastingClicked', async (event) => { - appendElement('onActivitiesPanelStartBroadcastingClicked'); + webComponent.addEventListener('onBroadcastingStartRequested', async (event) => { + const { roomName, broadcastUrl } = event.detail; + appendElement(`onBroadcastingStartRequested-${roomName}-${broadcastUrl}`); }); webComponent.addEventListener('onActivitiesPanelStopBroadcastingClicked', async (event) => { appendElement('onActivitiesPanelStopBroadcastingClicked'); }); - webComponent.addEventListener('onSessionCreated', (event) => { - var session = event.detail; - appendElement('onSessionCreated'); + webComponent.addEventListener('onRoomCreated', (event) => { + var room = event.detail; + appendElement('onRoomCreated'); - // You can see the session documentation here - // https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Session.html - - session.on('connectionCreated', (e) => { - var user = JSON.parse(e.connection.data).clientData; - appendElement(`${user}-connectionCreated`); + room.on('disconnected', (e) => { + appendElement('roomDisconnected'); }); - - session.on('sessionDisconnected', (e) => { - var user = JSON.parse(e.target.connection.data).clientData; - appendElement(user + '-sessionDisconnected'); - }); - - session.on('exception', (e) => appendElement(e.name)); }); webComponent.addEventListener('onParticipantCreated', (event) => { var participant = event.detail; - appendElement(`${participant.nickname}-onParticipantCreated`); + appendElement(`${participant.name}-onParticipantCreated`); }); - // webComponent.addEventListener('error', (event) => { - // console.log('Error event', event.detail); - // }); - - joinSession(SESSION_NAME, PARTICIPANT_NAME); + setWebcomponentAttributes(); }); -function appendElement(id) { - var eventsDiv = document.getElementById('events'); - var element = document.createElement('div'); - element.setAttribute('id', id); - element.setAttribute('style', 'height: 1px;'); - eventsDiv.appendChild(element); -} - -async function joinSession(sessionName, participantName) { +function setWebcomponentAttributes() { var webComponent = document.querySelector('openvidu-webcomponent'); - var tokens; - - if (FAKE_DEVICES) { - monkeyPatchMediaDevices(); - } - - if (SINGLE_TOKEN) { - tokens = await getToken(sessionName); - } else { - tokens = { webcam: await getToken(sessionName), screen: await getToken(sessionName) }; - } + webComponent.participantName = PARTICIPANT_NAME; webComponent.minimal = MINIMAL; webComponent.lang = LANG; - webComponent.captionsLang = CAPTIONS_LANG; if (CUSTOM_LANG_OPTIONS) { webComponent.langOptions = [ { name: 'Esp', lang: 'es' }, { name: 'Eng', lang: 'en' } ]; } - if (CUSTOM_CAPTIONS_LANG_OPTIONS) { - webComponent.captionsLangOptions = [ - { name: 'Esp', lang: 'es-ES' }, - { name: 'Eng', lang: 'en-US' } - ]; + // TODO: Uncomment when the captions are implemented + // webComponent.captionsLang = CAPTIONS_LANG; + // if (CUSTOM_CAPTIONS_LANG_OPTIONS) { + // webComponent.captionsLangOptions = [ + // { name: 'Esp', lang: 'es-ES' }, + // { name: 'Eng', lang: 'en-US' } + // ]; + // } + if (FAKE_DEVICES) { + console.warn('Using fake devices'); + monkeyPatchMediaDevices(); + } + if (FAKE_RECORDINGS) { + console.warn('Using fake recordings'); + webComponent.recordingActivityRecordingsList = [{ status: 'ready', filename: 'fakeRecording' }]; + } + + if (BROADCASTING_ERROR) { + webComponent.broadcastingActivityBroadcastingError = { message: BROADCASTING_ERROR, broadcastAvailable: true }; } webComponent.prejoin = PREJOIN; - webComponent.videoMuted = VIDEO_MUTED; - webComponent.audioMuted = AUDIO_MUTED; + webComponent.videoEnabled = VIDEO_ENABLED; + webComponent.audioEnabled = AUDIO_ENABLED; webComponent.toolbarScreenshareButton = SCREENSHARE_BUTTON; webComponent.toolbarFullscreenButton = FULLSCREEN_BUTTON; webComponent.toolbarSettingsButton = TOOLBAR_SETTINGS_BUTTON; - webComponent.toolbarCaptionsButton = CAPTIONS_BUTTON; + // webComponent.toolbarCaptionsButton = CAPTIONS_BUTTON; webComponent.toolbarLeaveButton = LEAVE_BUTTON; webComponent.toolbarRecordingButton = RECORDING_BUTTON; webComponent.toolbarBroadcastingButton = BROADCASTING_BUTTON; @@ -246,125 +235,58 @@ async function joinSession(sessionName, participantName) { webComponent.toolbarChatPanelButton = CHAT_PANEL_BUTTON; webComponent.toolbarParticipantsPanelButton = PARTICIPANTS_PANEL_BUTTON; webComponent.toolbarDisplayLogo = DISPLAY_LOGO; - webComponent.toolbarDisplaySessionName = DISPLAY_SESSION_NAME; + webComponent.toolbarDisplayRoomName = DISPLAY_ROOM_NAME; webComponent.streamDisplayParticipantName = DISPLAY_PARTICIPANT_NAME; webComponent.streamDisplayAudioDetection = DISPLAY_AUDIO_DETECTION; - webComponent.streamSettingsButton = SETTINGS_BUTTON; + webComponent.streamVideoControls = VIDEO_CONTROLS; webComponent.participantPanelItemMuteButton = PARTICIPANT_MUTE_BUTTON; - webComponent.recordingActivityRecordingsList = [{ status: 'ready' }]; webComponent.activitiesPanelRecordingActivity = ACTIVITIES_RECORDING_ACTIVITY; webComponent.activitiesPanelBroadcastingActivity = ACTIVITIES_BROADCASTING_ACTIVITY; webComponent.recordingActivityRecordingError = RECORDING_ERROR; - - webComponent.broadcastingActivityBroadcastingError = { message: BROADCASTING_ERROR, broadcastAvailable: true }; - - webComponent.participantName = participantName; - webComponent.tokens = tokens; } -/** - * -------------------------- - * SERVER-SIDE RESPONSIBILITY - * -------------------------- - * These methods retrieve the mandatory user token from OpenVidu Server. - * This behavior MUST BE IN YOUR SERVER-SIDE IN PRODUCTION (by using - * the API REST, openvidu-java-client or openvidu-node-client): - * 1) Initialize a session in OpenVidu Server (POST /api/sessions) - * 2) Generate a token in OpenVidu Server (POST /api/tokens) - * 3) Configure OpenVidu Web Component in your client side with the token - */ - -function getToken(sessionName) { - return createSession(sessionName).then((sessionId) => createToken(sessionId)); +function appendElement(id) { + var eventsDiv = document.getElementById('events'); + eventsDiv.setAttribute('style', 'position: absolute;'); + var element = document.createElement('div'); + element.setAttribute('id', id); + element.setAttribute('style', 'height: 1px;'); + eventsDiv.appendChild(element); } -function createSession(sessionName) { - // See https://docs.openvidu.io/en/stable/reference-docs/REST-API/#post-apisessions - return new Promise((resolve, reject) => { - $.ajax({ - type: 'POST', - url: OPENVIDU_SERVER_URL + '/openvidu/api/sessions', - data: JSON.stringify({ customSessionId: sessionName }), +async function joinSession(roomName, participantName) { + var webComponent = document.querySelector('openvidu-webcomponent'); + console.log('Joining session', roomName, participantName); + try { + webComponent.token = await getToken(roomName, participantName); + } catch (error) { + webComponent.tokenError = error; + } +} + +async function getToken(roomName, participantName) { + try { + const response = await fetch(OPENVIDU_CALL_SERVER_URL + '/call/api/rooms', { + method: 'POST', headers: { - Authorization: 'Basic ' + btoa('OPENVIDUAPP:' + OPENVIDU_SECRET), 'Content-Type': 'application/json' + // 'Authorization': 'Basic ' + btoa('OPENVIDUAPP:' + OPENVIDU_SECRET), }, - success: (response) => resolve(response.id), - error: (error) => { - if (error.status === 409) { - resolve(sessionName); - } else { - console.warn('No connection to OpenVidu Server. This may be a certificate error at ' + OPENVIDU_SERVER_URL); - if ( - window.confirm( - 'No connection to OpenVidu Server. This may be a certificate error at "' + - OPENVIDU_SERVER_URL + - '"\n\nClick OK to navigate and accept it. ' + - 'If no certificate warning is shown, then check that your OpenVidu Server is up and running at "' + - OPENVIDU_SERVER_URL + - '"' - ) - ) { - location.assign(OPENVIDU_SERVER_URL + '/openvidu/accept-certificate'); - } - } - } + body: JSON.stringify({ + participantName, + roomName + }) }); - }); + + if (!response.ok) { + throw new Error('Failed to fetch token'); + } + + const data = await response.json(); + return data.token; + } catch (error) { + console.error(error); + throw error; + } } - -function createToken(sessionId) { - // See https://docs.openvidu.io/en/stable/reference-docs/REST-API/#post-apitokens - return new Promise((resolve, reject) => { - $.ajax({ - type: 'POST', - url: `${OPENVIDU_SERVER_URL}/openvidu/api/sessions/${sessionId}/connection`, - data: JSON.stringify({ session: sessionId, role: 'MODERATOR' }), - headers: { - Authorization: 'Basic ' + btoa('OPENVIDUAPP:' + OPENVIDU_SECRET), - 'Content-Type': 'application/json' - }, - success: (response) => { - resolve(response.token); - }, - error: (error) => reject(error) - }); - }); -} - -// function startRecording(sessionId) { -// return new Promise((resolve, reject) => { -// $.ajax({ -// type: 'POST', -// url: `${OPENVIDU_SERVER_URL}/openvidu/api/recordings/start`, -// data: JSON.stringify({ session: sessionId }), -// headers: { -// Authorization: 'Basic ' + btoa('OPENVIDUAPP:' + OPENVIDU_SERVER_SECRET), -// 'Content-Type': 'application/json', -// }, -// success: (response) => {console.log(response); resolve(response.id)}, -// error: (error) => { -// reject(error) -// }, -// }); -// }); - -// } - -// function stopRecording(recordingId) { -// return new Promise((resolve, reject) => { -// $.ajax({ -// type: 'POST', -// url: `${OPENVIDU_SERVER_URL}/openvidu/api/recordings/stop/${recordingId}`, -// headers: { -// Authorization: 'Basic ' + btoa('OPENVIDUAPP:' + OPENVIDU_SERVER_SECRET), -// 'Content-Type': 'application/json', -// }, -// success: (response) => resolve(response), -// error: (error) => { -// reject(error) -// }, -// }); -// }); -// } diff --git a/openvidu-components-angular/e2e/webcomponent-app/index.html b/openvidu-components-angular/e2e/webcomponent-app/index.html index e60d26c6..93fe7d45 100644 --- a/openvidu-components-angular/e2e/webcomponent-app/index.html +++ b/openvidu-components-angular/e2e/webcomponent-app/index.html @@ -2,14 +2,8 @@ openvidu-web-component - - - + diff --git a/openvidu-components-angular/e2e/webcomponent-app/utils/media-devices.js b/openvidu-components-angular/e2e/webcomponent-app/utils/media-devices.js index 4467d661..1e901209 100644 --- a/openvidu-components-angular/e2e/webcomponent-app/utils/media-devices.js +++ b/openvidu-components-angular/e2e/webcomponent-app/utils/media-devices.js @@ -6,20 +6,28 @@ export default function monkeyPatchMediaDevices() { const getUserMediaFn = MediaDevices.prototype.getUserMedia; const getDisplayMediaFn = MediaDevices.prototype.getDisplayMedia; - const fakeDevice = { + const fakeVideoDevice = { deviceId: 'virtual', groupID: '', kind: 'videoinput', label: 'custom_fake_video_1' }; - MediaDevices.prototype.enumerateDevices = async function () { + const fakeAudioDevice = { + deviceId: 'virtual', + groupID: '', + kind: 'audioinput', + label: 'custom_fake_audio_1' + }; + + const enumerateDevicesMonkeyPatch = async function () { const res = await enumerateDevicesFn.call(navigator.mediaDevices); - res.push(fakeDevice); + res.push(fakeVideoDevice); + res.push(fakeAudioDevice); return res; }; - MediaDevices.prototype.getUserMedia = async function () { + const getUserMediaMonkeyPatch = async function () { const args = arguments[0]; const { deviceId, advanced, width, height } = args.video; if (deviceId === 'virtual' || deviceId?.exact === 'virtual') { @@ -35,7 +43,7 @@ export default function monkeyPatchMediaDevices() { const res = await getUserMediaFn.call(navigator.mediaDevices, constraints); if (res) { - const filter = new FilterStream(res, fakeDevice.label); + const filter = new FilterStream(res, fakeVideoDevice.label); return filter.outputStream; } @@ -45,10 +53,10 @@ export default function monkeyPatchMediaDevices() { return getUserMediaFn.call(navigator.mediaDevices, ...arguments); }; - MediaDevices.prototype.getDisplayMedia = async function () { + const getDisplayMediaMonkeyPatch = async function () { const { video, audio } = arguments[0]; - const screenVideoElement = document.getElementsByClassName("OT_video-element screen-type")[0]; + const screenVideoElement = document.getElementsByClassName('OV_video-element screen-type')[0]; const currentTrackLabel = screenVideoElement?.srcObject?.getVideoTracks()[0]?.label; const res = await getDisplayMediaFn.call(navigator.mediaDevices, { video, audio }); @@ -59,4 +67,11 @@ export default function monkeyPatchMediaDevices() { return res; }; + + MediaDevices.prototype.enumerateDevices = enumerateDevicesMonkeyPatch; + navigator.mediaDevices.enumerateDevices = enumerateDevicesMonkeyPatch; + MediaDevices.prototype.getUserMedia = getUserMediaMonkeyPatch; + navigator.mediaDevices.getUserMedia = getUserMediaMonkeyPatch; + MediaDevices.prototype.getDisplayMedia = getDisplayMediaMonkeyPatch; + navigator.mediaDevices.getDisplayMedia = getDisplayMediaMonkeyPatch; } diff --git a/openvidu-components-angular/e2e/webcomponent-e2e/api-directives.test.ts b/openvidu-components-angular/e2e/webcomponent-e2e/api-directives.test.ts new file mode 100644 index 00000000..21717968 --- /dev/null +++ b/openvidu-components-angular/e2e/webcomponent-e2e/api-directives.test.ts @@ -0,0 +1,673 @@ +import { expect } from 'chai'; +import { Builder, WebDriver } from 'selenium-webdriver'; +import { OPENVIDU_CALL_SERVER } from '../config'; +import { WebComponentConfig } from '../selenium.conf'; +import { OpenViduComponentsPO } from '../utils.po.test'; + +const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`; + +describe('Testing API Directives', () => { + let browser: WebDriver; + let utils: OpenViduComponentsPO; + async function createChromeBrowser(): Promise { + return await new Builder() + .forBrowser(WebComponentConfig.browserName) + .withCapabilities(WebComponentConfig.browserCapabilities) + .setChromeOptions(WebComponentConfig.browserOptions) + .usingServer(WebComponentConfig.seleniumAddress) + .build(); + } + + beforeEach(async () => { + browser = await createChromeBrowser(); + utils = new OpenViduComponentsPO(browser); + }); + + afterEach(async () => { + // console.log('data:image/png;base64,' + await browser.takeScreenshot()); + await browser.quit(); + }); + + it('should set the MINIMAL UI', async () => { + await browser.get(`${url}&minimal=true`); + // Checking if prejoin page exist + await utils.checkPrejoinIsPresent(); + + // Checking if audio detection is not displayed + expect(await utils.isPresent('#audio-wave-container')).to.be.false; + + const joinButton = await utils.waitForElement('#join-button'); + await joinButton.click(); + + // Checking if session container is present + await utils.checkSessionIsPresent(); + + // Checking if layout is present + await utils.checkLayoutPresent(); + + // Checking if stream component is present + utils.checkStreamIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + // Checking if screenshare button is not present + expect(await utils.isPresent('#screenshare-btn')).to.be.false; + + // Checking if more options button is not present + expect(await utils.isPresent('#more-options-btn')).to.be.false; + + // Checking if participants panel button is not present + expect(await utils.isPresent('#participants-panel-btn')).to.be.false; + + // Checking if activities panel button is not present + expect(await utils.isPresent('#activities-panel-btn')).to.be.false; + + // Checking if logo is not displayed + expect(await utils.isPresent('#branding-logo')).to.be.false; + + // Checking if session name is not displayed + expect(await utils.isPresent('#session-name')).to.be.false; + + // Checking if nickname is not displayed + expect(await utils.getNumberOfElements('#participant-name-container')).equals(0); + + // Checking if audio detection is not displayed + expect(await utils.isPresent('#audio-wave-container')).to.be.false; + + // Checking if settings button is not displayed + expect(await utils.isPresent('#settings-container')).to.be.false; + }); + + it('should change the UI LANG in prejoin page', async () => { + await browser.get(`${url}&lang=es`); + + await utils.checkPrejoinIsPresent(); + + await utils.waitForElement('#lang-btn-compact'); + + const element = await utils.waitForElement('#join-button'); + expect(await element.getText()).equal('Unirme ahora'); + }); + + it('should change the UI LANG in room page', async () => { + await browser.get(`${url}&prejoin=false&lang=es`); + + await utils.checkLayoutPresent(); + await utils.checkToolbarIsPresent(); + + await utils.togglePanel('settings'); + + await utils.waitForElement('.sidenav-menu'); + expect(await utils.isPresent('#default-settings-panel')).to.be.true; + const panelTitle = await utils.waitForElement('.panel-title'); + expect(await panelTitle.getText()).equal('Configuración'); + + const element = await utils.waitForElement('#lang-selected-name'); + expect(await element.getAttribute('innerText')).equal('Español'); + }); + + it('should override the LANG OPTIONS', async () => { + await browser.get(`${url}&prejoin=true&langOptions=true`); + + await utils.checkPrejoinIsPresent(); + await utils.waitForElement('#lang-btn-compact'); + await utils.clickOn('#lang-btn-compact'); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.lang-menu-opt')).equals(2); + + await utils.clickOn('.lang-menu-opt'); + await browser.sleep(500); + + await utils.clickOn('#join-button'); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + await utils.togglePanel('settings'); + + await browser.sleep(500); + + await utils.waitForElement('#settings-container'); + await utils.waitForElement('.lang-button'); + await utils.clickOn('.lang-button'); + + await browser.sleep(500); + + expect(await utils.getNumberOfElements('.lang-menu-opt')).equals(2); + }); + + it('should show the PREJOIN page', async () => { + await browser.get(`${url}&prejoin=true`); + await utils.checkPrejoinIsPresent(); + }); + + it('should not show the PREJOIN page', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + }); + + it('should join to Room', async () => { + await browser.get(`${url}`); + + // Checking if prejoin page exist + await utils.checkPrejoinIsPresent(); + + const joinButton = await utils.waitForElement('#join-button'); + await joinButton.click(); + + // Checking if session container is present + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + + // Checking if screenshare button is not present + expect(await utils.isPresent('#screenshare-btn')).to.be.true; + }); + + it('should show the token error WITH prejoin page', async () => { + const fixedUrl = `${url}&roomName=TEST_TOKEN&participantName=PNAME`; + await browser.get(`${fixedUrl}`); + + // Checking if prejoin page exist + await utils.checkPrejoinIsPresent(); + + await utils.waitForElement('#join-button'); + await utils.clickOn('#join-button'); + + // Checking if session container is present + await utils.checkSessionIsPresent(); + + // Starting new browser for adding a new participant + const newTabScript = `window.open("${fixedUrl}")`; + await browser.executeScript(newTabScript); + + // Go to first tab + const tabs = await browser.getAllWindowHandles(); + browser.switchTo().window(tabs[1]); + + await utils.checkPrejoinIsPresent(); + await utils.waitForElement('#join-button'); + await utils.clickOn('#join-button'); + + // Checking if token error is displayed + await utils.waitForElement('#token-error'); + expect(await utils.isPresent('#token-error')).to.be.true; + }); + + it('should show the token error WITHOUT prejoin page', async () => { + const fixedUrl = `${url}&roomName=TOKEN_ERROR&prejoin=false&participantName=PNAME`; + await browser.get(`${fixedUrl}`); + + // Checking if session container is present + await utils.checkSessionIsPresent(); + + // Starting new browser for adding a new participant + const newTabScript = `window.open("${fixedUrl}")`; + await browser.executeScript(newTabScript); + + // Go to first tab + const tabs = await browser.getAllWindowHandles(); + browser.switchTo().window(tabs[1]); + + // Checking if token error is displayed + await utils.waitForElement('#openvidu-dialog'); + expect(await utils.isPresent('#openvidu-dialog')).to.be.true; + }); + + it('should run the app with VIDEO DISABLED in prejoin page', async () => { + await browser.get(`${url}&prejoin=true&videoEnabled=false`); + + await utils.checkPrejoinIsPresent(); + + // Checking if video is displayed + expect(await utils.getNumberOfElements('video')).equals(1); + + // Checking if virtual background button is disabled + // const button = await utils.waitForElement('#background-effects-btn'); + // expect(await button.isEnabled()).to.be.false; + + await utils.waitForElement('#videocam_off'); + await utils.clickOn('#join-button'); + + await utils.checkSessionIsPresent(); + + expect(await utils.getNumberOfElements('video')).equals(1); + + await utils.waitForElement('#videocam_off'); + expect(await utils.isPresent('#videocam_off')).to.be.true; + }); + + it('should run the app with VIDEO DISABLED and WITHOUT PREJOIN page', async () => { + await browser.get(`${url}&prejoin=false&videoEnabled=false`); + + await utils.checkSessionIsPresent(); + + await utils.checkLayoutPresent(); + + // Checking if video is displayed + expect(await utils.getNumberOfElements('video')).equals(1); + expect(await utils.getNumberOfElements('#video-poster')).equals(1); + + await utils.waitForElement('#videocam_off'); + expect(await utils.isPresent('#videocam_off')).to.be.true; + }); + + it('should run the app with AUDIO DISABLED in prejoin page', async () => { + // let isAudioEnabled; + // const script = 'return document.getElementsByTagName("video")[0].srcObject.getAudioTracks()[0].enabled;'; + + await browser.get(`${url}&audioEnabled=false`); + + await utils.checkPrejoinIsPresent(); + + // Checking if video is displayed + await utils.checkVideoElementIsPresent(); + + // Checking if audio track is disabled/muted + // isAudioEnabled = await browser.executeScript(script); + // expect(isAudioEnabled).to.be.false; + + await utils.waitForElement('#mic_off'); + expect(await utils.isPresent('#mic_off')).to.be.true; + + await utils.clickOn('#join-button'); + + await utils.checkSessionIsPresent(); + // isAudioEnabled = await browser.executeScript(script); + // expect(isAudioEnabled).to.be.false; + + await utils.waitForElement('#mic_off'); + expect(await utils.isPresent('#mic_off')).to.be.true; + }); + + it('should run the app with AUDIO DISABLED and WITHOUT PREJOIN page', async () => { + // let isAudioEnabled; + // const audioEnableScript = 'return document.getElementsByTagName("video")[0].srcObject.getAudioTracks()[0].enabled;'; + + await browser.get(`${url}&prejoin=false&audioEnabled=false`); + + await utils.checkSessionIsPresent(); + + // Checking if video is displayed + await utils.checkVideoElementIsPresent(); + + // Checking if audio track is disabled/muted + // isAudioEnabled = await browser.executeScript(audioEnableScript); + // expect(isAudioEnabled).to.be.false; + + await utils.waitForElement('#mic_off'); + expect(await utils.isPresent('#mic_off')).to.be.true; + }); + + it('should HIDE the SCREENSHARE button', async () => { + await browser.get(`${url}&prejoin=false&screenshareBtn=false`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + // Checking if screenshare button is not present + expect(await utils.isPresent('#screenshare-btn')).to.be.false; + }); + + it('should HIDE the FULLSCREEN button', async () => { + await browser.get(`${url}&prejoin=false&fullscreenBtn=false`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + await utils.toggleToolbarMoreOptions(); + expect(await utils.getNumberOfElements('#fullscreen-btn')).equals(0); + }); + + it('should HIDE the CAPTIONS button', async () => { + await browser.get(`${url}&prejoin=false&toolbarCaptionsBtn=false`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + await utils.toggleToolbarMoreOptions(); + + // Checking if captions button is not present + expect(await utils.isPresent('#captions-btn')).to.be.false; + + await utils.clickOn('#toolbar-settings-btn'); + + await browser.sleep(500); + + await utils.waitForElement('.settings-container'); + expect(await utils.isPresent('.settings-container')).to.be.true; + + expect(await utils.isPresent('#captions-opt')).to.be.false; + }); + + it('should HIDE the TOOLBAR RECORDING button', async () => { + await browser.get(`${url}&prejoin=false&toolbarRecordingButton=false`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + await utils.toggleToolbarMoreOptions(); + + // Checking if recording button is not present + expect(await utils.isPresent('#recording-btn')).to.be.false; + }); + + it('should HIDE the TOOLBAR BROADCASTING button', async () => { + await browser.get(`${url}&prejoin=false&toolbarBroadcastingButton=false`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + await utils.toggleToolbarMoreOptions(); + + // Checking if broadcasting button is not present + expect(await utils.isPresent('#broadcasting-btn')).to.be.false; + }); + + it('should HIDE the TOOLBAR SETTINGS button', async () => { + await browser.get(`${url}&prejoin=false&toolbarSettingsBtn=false`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + // Open more options menu + await utils.toggleToolbarMoreOptions(); + + expect(await utils.isPresent('#toolbar-settings-btn')).to.be.false; + }); + + it('should HIDE the LEAVE button', async () => { + await browser.get(`${url}&prejoin=false&leaveBtn=false`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + // Checking if leave button is not present + expect(await utils.getNumberOfElements('#leave-btn')).equals(0); + }); + + it('should HIDE the ACTIVITIES PANEL button', async () => { + await browser.get(`${url}&prejoin=false&activitiesPanelBtn=false`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + // Checking if activities panel button is not present + expect(await utils.isPresent('#activities-panel-btn')).to.be.false; + }); + + it('should HIDE the CHAT PANEL button', async () => { + await browser.get(`${url}&prejoin=false&chatPanelBtn=false`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + // Checking if chat panel button is not present + expect(await utils.isPresent('#chat-panel-btn')).to.be.false; + }); + + it('should HIDE the PARTICIPANTS PANEL button', async () => { + await browser.get(`${url}&prejoin=false&participantsPanelBtn=false`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + // Checking if participants panel button is not present + expect(await utils.isPresent('#participants-panel-btn')).to.be.false; + }); + + it('should HIDE the LOGO', async () => { + await browser.get(`${url}&prejoin=false&displayLogo=false`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + // Checking if toolbar is present + await utils.waitForElement('#info-container'); + expect(await utils.isPresent('#info-container')).to.be.true; + + // Checking if logo is not displayed + expect(await utils.isPresent('#branding-logo')).to.be.false; + }); + + it('should HIDE the SESSION NAME', async () => { + await browser.get(`${url}&prejoin=false&displayRoomName=false`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + // Checking if toolbar is present + await utils.waitForElement('#info-container'); + expect(await utils.isPresent('#info-container')).to.be.true; + + // Checking if session name is not displayed + expect(await utils.isPresent('#session-name')).to.be.false; + }); + + it('should HIDE the PARTICIPANT NAME', async () => { + await browser.get(`${url}&prejoin=false&displayParticipantName=false`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + // Checking if stream component is present + await utils.checkStreamIsPresent(); + + // Checking if nickname is not present + expect(await utils.isPresent('#participant-name-container')).to.be.false; + }); + + it('should HIDE the AUDIO DETECTION element', async () => { + await browser.get(`${url}&prejoin=false&displayAudioDetection=false`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + // Checking if stream component is present + await utils.checkStreamIsPresent(); + + // Checking if audio detection is not present + expect(await utils.isPresent('#audio-wave-container')).to.be.false; + }); + + it('should HIDE the STREAM VIDEO CONTROLS button', async () => { + await browser.get(`${url}&prejoin=false&videoControls=false`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + // Checking if stream component is present + await utils.checkStreamIsPresent(); + + // Checking if settings button is not present + expect(await utils.isPresent('.stream-video-controls')).to.be.false; + }); + + it('should HIDE the MUTE button in participants panel', async () => { + const roomName = 'e2etest'; + const fixedUrl = `${url}&prejoin=false&participantMuteBtn=false&roomName=${roomName}`; + await browser.get(fixedUrl); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + const participantsButton = await utils.waitForElement('#participants-panel-btn'); + await participantsButton.click(); + + // Checking if participatns panel is displayed + await utils.waitForElement('#participants-container'); + expect(await utils.isPresent('#participants-container')).to.be.true; + + // Checking remote participants item + expect(await utils.isPresent('#remote-participant-item')).to.be.false; + + // Starting new browser for adding a new participant + const newTabScript = `window.open("${fixedUrl}")`; + await browser.executeScript(newTabScript); + + // Go to first tab + const tabs = await browser.getAllWindowHandles(); + browser.switchTo().window(tabs[0]); + + // Checking if mute button is not displayed in participant item + await utils.waitForElement('#remote-participant-item'); + expect(await utils.isPresent('#remote-participant-item')).to.be.true; + + expect(await utils.isPresent('#mute-btn')).to.be.false; + }); + + it('should HIDE the RECORDING ACTIVITY in activities panel', async () => { + let element; + const fixedUrl = `${url}&prejoin=false&activitiesPanelRecordingActivity=false`; + await browser.get(fixedUrl); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + element = await utils.waitForElement('#activities-panel-btn'); + await element.click(); + + // Checking if participatns panel is displayed + await utils.waitForElement('#default-activities-panel'); + expect(await utils.isPresent('#default-activities-panel')).to.be.true; + + // await browser.sleep(1000); + + // Checking if recording activity exists + await utils.waitForElement('.activities-body-container'); + expect(await utils.isPresent('ov-recording-activity')).to.be.false; + }); + + it('should SHOW a RECORDING ERROR in activities panel', async () => { + let element; + const fixedUrl = `${url}&prejoin=false&recordingError=TEST_ERROR`; + await browser.get(fixedUrl); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + element = await utils.waitForElement('#activities-panel-btn'); + await element.click(); + + // Checking if participatns panel is displayed + await utils.waitForElement('#default-activities-panel'); + expect(await utils.isPresent('#default-activities-panel')).to.be.true; + + // Checking if recording activity exists + await utils.waitForElement('#activities-container'); + await utils.waitForElement('.activities-body-container'); + + await utils.waitForElement('ov-recording-activity'); + expect(await utils.isPresent('ov-recording-activity')).to.be.true; + + await utils.waitForElement('.failed'); + expect(await utils.isPresent('.failed')).to.be.true; + + // Open recording + await browser.sleep(500); + await utils.waitForElement('ov-recording-activity'); + await utils.clickOn('ov-recording-activity'); + await browser.sleep(500); + element = await utils.waitForElement('.recording-error'); + expect(await element.getAttribute('innerText')).equal('"TEST_ERROR"'); + expect(await utils.isPresent('.recording-error')).to.be.true; + }); + + it('should SHOW a BROADCASTING ERROR in activities panel', async () => { + let element; + const fixedUrl = `${url}&prejoin=false&broadcastingError=TEST_ERROR`; + await browser.get(fixedUrl); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + element = await utils.waitForElement('#activities-panel-btn'); + await element.click(); + + // Checking if participatns panel is displayed + await utils.waitForElement('#default-activities-panel'); + expect(await utils.isPresent('#default-activities-panel')).to.be.true; + + // Checking if broadcasting activity exists + await utils.waitForElement('#activities-container'); + await utils.waitForElement('.activities-body-container'); + + await utils.waitForElement('ov-broadcasting-activity'); + expect(await utils.isPresent('ov-broadcasting-activity')).to.be.true; + + const status = await utils.waitForElement('#broadcasting-status'); + expect(await status.getAttribute('innerText')).equals('FAILED'); + + // Open broadcasting + await browser.sleep(500); + await utils.clickOn('ov-broadcasting-activity'); + await browser.sleep(500); + + element = await utils.waitForElement('#broadcasting-error'); + expect(await element.getAttribute('innerText')).equal('TEST_ERROR'); + }); + + it('should HIDE the BROADCASTING ACTIVITY in activities panel', async () => { + await browser.get(`${url}&prejoin=false&activitiesPanelBroadcastingActivity=false`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + await utils.waitForElement('#activities-panel-btn'); + await utils.clickOn('#activities-panel-btn'); + + // Checking if participatns panel is displayed + await utils.waitForElement('#default-activities-panel'); + expect(await utils.isPresent('#default-activities-panel')).to.be.true; + + // await browser.sleep(1000); + + // Checking if recording activity exists + await utils.waitForElement('.activities-body-container'); + expect(await utils.isPresent('ov-broadcasting-activity')).to.be.false; + }); +}); diff --git a/openvidu-components-angular/e2e/webcomponent-e2e/captions.test.ts b/openvidu-components-angular/e2e/webcomponent-e2e/captions.test.ts new file mode 100644 index 00000000..ebed496a --- /dev/null +++ b/openvidu-components-angular/e2e/webcomponent-e2e/captions.test.ts @@ -0,0 +1,181 @@ +import { expect } from 'chai'; +import { Builder, Key, WebDriver } from 'selenium-webdriver'; +import { OPENVIDU_CALL_SERVER } from '../config'; +import { WebComponentConfig } from '../selenium.conf'; +import { OpenViduComponentsPO } from '../utils.po.test'; + +const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`; + +//TODO: Uncomment when captions are implemented +// describe('Testing captions features', () => { +// let browser: WebDriver; +// let utils: OpenViduComponentsPO; +// async function createChromeBrowser(): Promise { +// return await new Builder() +// .forBrowser(WebComponentConfig.browserName) +// .withCapabilities(WebComponentConfig.browserCapabilities) +// .setChromeOptions(WebComponentConfig.browserOptions) +// .usingServer(WebComponentConfig.seleniumAddress) +// .build(); +// } + +// beforeEach(async () => { +// browser = await createChromeBrowser(); +// utils = new OpenViduComponentsPO(browser); +// }); + +// afterEach(async () => { +// await browser.quit(); +// }); + +// it('should OPEN the CAPTIONS container', async () => { +// await browser.get(`${url}&prejoin=false`); + +// await utils.checkSessionIsPresent(); + +// // Checking if toolbar is present +// await utils.checkToolbarIsPresent(); + +// // Open more options menu +// await utils.clickOn('#more-options-btn'); + +// await browser.sleep(500); + +// // Checking if button panel is present +// await utils.waitForElement('#more-options-menu'); +// expect(await utils.isPresent('#more-options-menu')).to.be.true; + +// // Checking if captions button is present +// await utils.waitForElement('#captions-btn'); +// expect(await utils.isPresent('#captions-btn')).to.be.true; +// await utils.clickOn('#captions-btn'); + +// await utils.waitForElement('.captions-container'); +// }); + +// it('should OPEN the SETTINGS panel from captions button', async () => { +// await browser.get(`${url}&prejoin=false`); + +// await utils.checkSessionIsPresent(); + +// // Checking if toolbar is present +// await utils.checkToolbarIsPresent(); + +// // Open more options menu +// await utils.clickOn('#more-options-btn'); + +// await browser.sleep(500); + +// // Checking if button panel is present +// await utils.waitForElement('#more-options-menu'); +// expect(await utils.isPresent('#more-options-menu')).to.be.true; + +// // Checking if captions button is present +// await utils.waitForElement('#captions-btn'); +// expect(await utils.isPresent('#captions-btn')).to.be.true; +// await utils.clickOn('#captions-btn'); + +// await utils.waitForElement('.captions-container'); +// await utils.waitForElement('#caption-settings-btn'); +// await utils.clickOn('#caption-settings-btn'); + +// await browser.sleep(500); + +// await utils.waitForElement('.settings-container'); +// expect(await utils.isPresent('.settings-container')).to.be.true; + +// await utils.waitForElement('ov-captions-settings'); + +// // Expect caption button is not present +// expect(await utils.isPresent('#caption-settings-btn')).to.be.false; +// }); + +// it('should TOGGLE the CAPTIONS container from settings panel', async () => { +// await browser.get(`${url}&prejoin=false`); + +// await utils.checkSessionIsPresent(); + +// // Checking if toolbar is present +// await utils.checkToolbarIsPresent(); + +// // Open more options menu +// await utils.clickOn('#more-options-btn'); + +// await browser.sleep(500); + +// // Checking if button panel is present +// await utils.waitForElement('#more-options-menu'); +// expect(await utils.isPresent('#more-options-menu')).to.be.true; + +// // Checking if captions button is present +// await utils.waitForElement('#captions-btn'); +// expect(await utils.isPresent('#captions-btn')).to.be.true; +// await utils.clickOn('#captions-btn'); + +// await utils.waitForElement('.captions-container'); +// await utils.waitForElement('#caption-settings-btn'); +// await utils.clickOn('#caption-settings-btn'); + +// await browser.sleep(500); + +// await utils.waitForElement('.settings-container'); +// expect(await utils.isPresent('.settings-container')).to.be.true; + +// await utils.waitForElement('ov-captions-settings'); + +// expect(await utils.isPresent('.captions-container')).to.be.true; +// await utils.clickOn('#captions-toggle-slide'); +// expect(await utils.isPresent('.captions-container')).to.be.false; + +// await browser.sleep(200); + +// await utils.clickOn('#captions-toggle-slide'); +// expect(await utils.isPresent('.captions-container')).to.be.true; +// }); + +// it('should change the CAPTIONS language', async () => { +// await browser.get(`${url}&prejoin=false`); + +// await utils.checkSessionIsPresent(); + +// // Checking if toolbar is present +// await utils.checkToolbarIsPresent(); + +// // Open more options menu +// await utils.clickOn('#more-options-btn'); + +// await browser.sleep(500); + +// // Checking if button panel is present +// await utils.waitForElement('#more-options-menu'); +// expect(await utils.isPresent('#more-options-menu')).to.be.true; + +// // Checking if captions button is present +// await utils.waitForElement('#captions-btn'); +// expect(await utils.isPresent('#captions-btn')).to.be.true; +// await utils.clickOn('#captions-btn'); + +// await utils.waitForElement('.captions-container'); +// await utils.waitForElement('#caption-settings-btn'); +// await utils.clickOn('#caption-settings-btn'); + +// await browser.sleep(500); + +// await utils.waitForElement('.settings-container'); +// expect(await utils.isPresent('.settings-container')).to.be.true; + +// await utils.waitForElement('ov-captions-settings'); + +// expect(await utils.isPresent('.captions-container')).to.be.true; + +// await utils.clickOn('.lang-button'); +// await browser.sleep(500); + +// await utils.clickOn('#es-ES'); +// await utils.clickOn('.panel-close-button'); + +// const button = await utils.waitForElement('#caption-settings-btn'); +// expect(await button.getText()).equals('settingsEspañol'); + +// }); +// }); \ No newline at end of file diff --git a/openvidu-components-angular/e2e/webcomponent-e2e/chat.test.ts b/openvidu-components-angular/e2e/webcomponent-e2e/chat.test.ts new file mode 100644 index 00000000..3b6d210f --- /dev/null +++ b/openvidu-components-angular/e2e/webcomponent-e2e/chat.test.ts @@ -0,0 +1,117 @@ +import { expect } from 'chai'; +import { Builder, WebDriver } from 'selenium-webdriver'; +import { OPENVIDU_CALL_SERVER } from '../config'; +import { WebComponentConfig } from '../selenium.conf'; +import { OpenViduComponentsPO } from '../utils.po.test'; + +const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`; + +describe('Testing CHAT features', () => { + let browser: WebDriver; + let utils: OpenViduComponentsPO; + + async function createChromeBrowser(): Promise { + return await new Builder() + .forBrowser(WebComponentConfig.browserName) + .withCapabilities(WebComponentConfig.browserCapabilities) + .setChromeOptions(WebComponentConfig.browserOptions) + .usingServer(WebComponentConfig.seleniumAddress) + .build(); + } + + beforeEach(async () => { + browser = await createChromeBrowser(); + utils = new OpenViduComponentsPO(browser); + }); + + afterEach(async () => { + await browser.quit(); + }); + + it('should send messages', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkLayoutPresent(); + + await utils.togglePanel('chat'); + await browser.sleep(500); + + await utils.waitForElement('.sidenav-menu'); + await utils.waitForElement('.input-container'); + expect(await utils.isPresent('.input-container')).to.be.true; + + const input = await utils.waitForElement('#chat-input'); + await input.sendKeys('Test message'); + + await utils.clickOn('#send-btn'); + + await utils.waitForElement('.message'); + await utils.getNumberOfElements('.message'); + expect(await utils.isPresent('.message')).to.be.true; + + expect(await utils.getNumberOfElements('.message')).equals(1); + + await input.sendKeys('Test message'); + await utils.clickOn('#send-btn'); + expect(await utils.getNumberOfElements('.message')).equals(2); + }); + + it('should receive a message', async () => { + const roomName = 'chattingE2E'; + let pName = `participant${Math.floor(Math.random() * 1000)}`; + const fixedUrl = `${url}&prejoin=false&roomName=${roomName}`; + await browser.get(fixedUrl); + await utils.checkLayoutPresent(); + + // Starting new browser for adding a new participant + const newTabScript = `window.open("${fixedUrl}&participantName=${pName}")`; + await browser.executeScript(newTabScript); + const tabs = await browser.getAllWindowHandles(); + browser.switchTo().window(tabs[1]); + + await utils.checkLayoutPresent(); + + await utils.togglePanel('chat'); + await browser.sleep(1000); + + await utils.waitForElement('.sidenav-menu'); + await utils.waitForElement('.input-container'); + expect(await utils.isPresent('.input-container')).to.be.true; + + const input = await utils.waitForElement('#chat-input'); + await input.sendKeys('test message'); + await utils.clickOn('#send-btn'); + + // Go to first tab + browser.switchTo().window(tabs[0]); + + await utils.waitForElement('.snackbarNotification'); + await utils.togglePanel('chat'); + await browser.sleep(1000); + await utils.waitForElement('.message'); + const participantName = await utils.waitForElement('.participant-name-container>p'); + expect(await utils.getNumberOfElements('.message')).equals(1); + expect(await participantName.getText()).equals(pName); + }); + + it('should send an url message and converts in a link', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkLayoutPresent(); + + await utils.togglePanel('chat'); + await browser.sleep(500); + + await utils.waitForElement('.sidenav-menu'); + await utils.waitForElement('.input-container'); + expect(await utils.isPresent('.input-container')).to.be.true; + + const input = await utils.waitForElement('#chat-input'); + await input.sendKeys('demos.openvidu.io'); + + await utils.clickOn('#send-btn'); + + await utils.waitForElement('.msg-content a'); + expect(await utils.isPresent('.msg-content a')).to.be.true; + }); +}); diff --git a/openvidu-components-angular/e2e/webcomponent-e2e/events.test.ts b/openvidu-components-angular/e2e/webcomponent-e2e/events.test.ts new file mode 100644 index 00000000..e0cea8d7 --- /dev/null +++ b/openvidu-components-angular/e2e/webcomponent-e2e/events.test.ts @@ -0,0 +1,644 @@ +import { expect } from 'chai'; +import { Builder, Key, WebDriver } from 'selenium-webdriver'; +import { OPENVIDU_CALL_SERVER } from '../config'; +import { WebComponentConfig } from '../selenium.conf'; +import { OpenViduComponentsPO } from '../utils.po.test'; + +const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`; + +describe('Testing videoconference EVENTS', () => { + let browser: WebDriver; + let utils: OpenViduComponentsPO; + const isHeadless: boolean = (WebComponentConfig.browserOptions as any).options_.args.includes('--headless'); + async function createChromeBrowser(): Promise { + return await new Builder() + .forBrowser(WebComponentConfig.browserName) + .withCapabilities(WebComponentConfig.browserCapabilities) + .setChromeOptions(WebComponentConfig.browserOptions) + .usingServer(WebComponentConfig.seleniumAddress) + .build(); + } + + beforeEach(async () => { + browser = await createChromeBrowser(); + utils = new OpenViduComponentsPO(browser); + }); + + afterEach(async () => { + await browser.quit(); + }); + + it('should receive the onReadyToJoin event', async () => { + await browser.get(`${url}`); + + await utils.waitForElement('#prejoin-container'); + expect(await utils.isPresent('#prejoin-container')).to.be.true; + + // Clicking to join button + await utils.waitForElement('#join-button'); + await utils.clickOn('#join-button'); + + // Checking if onReadyToJoin has been received + await utils.waitForElement('#onReadyToJoin'); + expect(await utils.isPresent('#onReadyToJoin')).to.be.true; + }); + + it('should receive the onTokenRequested event', async () => { + await browser.get(`${url}`); + + await utils.waitForElement('#prejoin-container'); + expect(await utils.isPresent('#prejoin-container')).to.be.true; + + // Clicking to join button + await utils.waitForElement('#join-button'); + await utils.clickOn('#join-button'); + + // Checking if onTokenRequested has been received + await utils.waitForElement('#onTokenRequested'); + expect(await utils.isPresent('#onTokenRequested')).to.be.true; + }); + + it('should receive the onRoomDisconnected event', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + + // Clicking to leave button + const leaveButton = await utils.waitForElement('#leave-btn'); + expect(await utils.isPresent('#leave-btn')).to.be.true; + await leaveButton.click(); + + // Checking if onRoomDisconnected has been received + await utils.waitForElement('#onRoomDisconnected'); + expect(await utils.isPresent('#onRoomDisconnected')).to.be.true; + }); + + it('should receive the onVideoEnabledChanged event when clicking on the prejoin', async () => { + await browser.get(url); + await utils.checkPrejoinIsPresent(); + + await utils.waitForElement('#camera-button'); + await utils.clickOn('#camera-button'); + + // Checking if onVideoEnabledChanged has been received + await utils.waitForElement('#onVideoEnabledChanged-false'); + expect(await utils.isPresent('#onVideoEnabledChanged-false')).to.be.true; + }); + + it('should receive the onVideoEnabledChanged event when clicking on the toolbar', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + + // Clicking to leave button + await utils.waitForElement('#camera-btn'); + await utils.clickOn('#camera-btn'); + + // Checking if onVideoEnabledChanged has been received + await utils.waitForElement('#onVideoEnabledChanged-false'); + expect(await utils.isPresent('#onVideoEnabledChanged-false')).to.be.true; + + await utils.clickOn('#camera-btn'); + await utils.waitForElement('#onVideoEnabledChanged-true'); + expect(await utils.isPresent('#onVideoEnabledChanged-true')).to.be.true; + }); + + it('should receive the onVideoEnabledChanged event when clicking on the settings panel', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + await utils.togglePanel('settings'); + await browser.sleep(500); + + await utils.waitForElement('#settings-container'); + await utils.clickOn('#video-opt'); + + await utils.waitForElement('ov-video-devices-select'); + await utils.clickOn('ov-video-devices-select #camera-button'); + // Checking if onVideoEnabledChanged has been received + await utils.waitForElement('#onVideoEnabledChanged-false'); + expect(await utils.isPresent('#onVideoEnabledChanged-false')).to.be.true; + + await utils.clickOn('ov-video-devices-select #camera-button'); + await utils.waitForElement('#onVideoEnabledChanged-true'); + expect(await utils.isPresent('#onVideoEnabledChanged-true')).to.be.true; + }); + + it('should receive the onVideoDeviceChanged event on prejoin', async () => { + await browser.get(`${url}&fakeDevices=true`); + await utils.checkPrejoinIsPresent(); + + await utils.waitForElement('#video-devices-form'); + await utils.clickOn('#video-devices-form'); + + await utils.waitForElement('#option-custom_fake_video_1'); + await utils.clickOn('#option-custom_fake_video_1'); + + await utils.waitForElement('#onVideoDeviceChanged'); + expect(await utils.isPresent('#onVideoDeviceChanged')).to.be.true; + }); + + it('should receive the onVideoDeviceChanged event on settings panel', async () => { + await browser.get(`${url}&prejoin=false&fakeDevices=true`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + await utils.togglePanel('settings'); + await browser.sleep(500); + + await utils.waitForElement('#settings-container'); + await utils.clickOn('#video-opt'); + + await utils.waitForElement('ov-video-devices-select'); + await utils.waitForElement('#video-devices-form'); + await utils.clickOn('#video-devices-form'); + + await utils.waitForElement('#option-custom_fake_video_1'); + await utils.clickOn('#option-custom_fake_video_1'); + + await utils.waitForElement('#onVideoDeviceChanged'); + expect(await utils.isPresent('#onVideoDeviceChanged')).to.be.true; + }); + + it('should receive the onAudioEnabledChanged event when clicking on the prejoin', async () => { + await browser.get(url); + await utils.checkPrejoinIsPresent(); + + await utils.waitForElement('#microphone-button'); + await utils.clickOn('#microphone-button'); + + // Checking if onAudioEnabledChanged has been received + await utils.waitForElement('#onAudioEnabledChanged-false'); + expect(await utils.isPresent('#onAudioEnabledChanged-false')).to.be.true; + }); + + it('should receive the onAudioEnabledChanged event when clicking on the toolbar', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + + // Clicking to leave button + await utils.waitForElement('#mic-btn'); + await utils.clickOn('#mic-btn'); + + // Checking if onAudioEnabledChanged has been received + await utils.waitForElement('#onAudioEnabledChanged-false'); + expect(await utils.isPresent('#onAudioEnabledChanged-false')).to.be.true; + + await utils.clickOn('#mic-btn'); + await utils.waitForElement('#onAudioEnabledChanged-true'); + expect(await utils.isPresent('#onAudioEnabledChanged-true')).to.be.true; + }); + + it('should receive the onAudioEnabledChanged event when clicking on the settings panel', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + await utils.togglePanel('settings'); + await browser.sleep(500); + + await utils.waitForElement('#settings-container'); + await utils.clickOn('#audio-opt'); + + await utils.waitForElement('ov-audio-devices-select'); + await utils.clickOn('ov-audio-devices-select #microphone-button'); + // Checking if onAudioEnabledChanged has been received + await utils.waitForElement('#onAudioEnabledChanged-false'); + expect(await utils.isPresent('#onAudioEnabledChanged-false')).to.be.true; + + await utils.clickOn('ov-audio-devices-select #microphone-button'); + await utils.waitForElement('#onAudioEnabledChanged-true'); + expect(await utils.isPresent('#onAudioEnabledChanged-true')).to.be.true; + }); + + it('should receive the onAudioDeviceChanged event on prejoin', async () => { + await browser.get(`${url}&fakeDevices=true`); + await utils.checkPrejoinIsPresent(); + + await utils.waitForElement('#audio-devices-form'); + await utils.clickOn('#audio-devices-form'); + + await utils.waitForElement('#option-custom_fake_audio_1'); + await utils.clickOn('#option-custom_fake_audio_1'); + + await utils.waitForElement('#onAudioDeviceChanged'); + expect(await utils.isPresent('#onAudioDeviceChanged')).to.be.true; + }); + + it('should receive the onAudioDeviceChanged event on settings panel', async () => { + await browser.get(`${url}&prejoin=false&fakeDevices=true`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + await utils.togglePanel('settings'); + await browser.sleep(500); + + await utils.waitForElement('#settings-container'); + await utils.clickOn('#audio-opt'); + + await utils.waitForElement('ov-audio-devices-select'); + await utils.waitForElement('#audio-devices-form'); + await utils.clickOn('#audio-devices-form'); + + await utils.waitForElement('#option-custom_fake_audio_1'); + await utils.clickOn('#option-custom_fake_audio_1'); + + await utils.waitForElement('#onAudioDeviceChanged'); + expect(await utils.isPresent('#onAudioDeviceChanged')).to.be.true; + }); + + it('should receive the onLangChanged event on prejoin', async () => { + await browser.get(`${url}`); + await utils.checkPrejoinIsPresent(); + + await utils.waitForElement('#lang-btn-compact'); + await utils.clickOn('#lang-btn-compact'); + + await browser.sleep(500); + await utils.clickOn('#lang-opt-es'); + await browser.sleep(500); + + await utils.waitForElement('#onLangChanged-es'); + expect(await utils.isPresent('#onLangChanged-es')).to.be.true; + }); + + it('should receive the onLangChanged event on settings panel', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + await utils.togglePanel('settings'); + await browser.sleep(500); + + await utils.waitForElement('#settings-container'); + await utils.waitForElement('.lang-button'); + await utils.clickOn('.lang-button'); + + await browser.sleep(500); + await utils.clickOn('#lang-opt-es'); + await browser.sleep(500); + + await utils.waitForElement('#onLangChanged-es'); + expect(await utils.isPresent('#onLangChanged-es')).to.be.true; + }); + + it('should receive the onScreenShareEnabledChanged event', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + + // Clicking to leave button + const screenshareButton = await utils.waitForElement('#screenshare-btn'); + expect(await utils.isPresent('#screenshare-btn')).to.be.true; + await screenshareButton.click(); + + // Checking if onScreenShareEnabledChanged has been received + await utils.waitForElement('#onScreenShareEnabledChanged'); + expect(await utils.isPresent('#onScreenShareEnabledChanged')).to.be.true; + }); + + // With headless mode, the Fullscreen API doesn't work + (isHeadless ? it.skip : it)('should receive the onFullscreenEnabledChanged event', async () => { + let element; + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + + await utils.toggleFullscreenFromToolbar(); + await browser.sleep(500); + + // Checking if onFullscreenEnabledChanged has been received + await utils.waitForElement('#onFullscreenEnabledChanged-true'); + expect(await utils.isPresent('#onFullscreenEnabledChanged-true')).to.be.true; + + await (await utils.waitForElement('html')).sendKeys(Key.F11); + await browser.sleep(500); + + await utils.waitForElement('#onFullscreenEnabledChanged-false'); + expect(await utils.isPresent('#onFullscreenEnabledChanged-false')).to.be.true; + }); + + it('should receive the onChatPanelStatusChanged event', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + + await utils.togglePanel('chat'); + + // Checking if onChatPanelStatusChanged has been received + await utils.waitForElement('#onChatPanelStatusChanged-true'); + expect(await utils.isPresent('#onChatPanelStatusChanged-true')).to.be.true; + + await utils.togglePanel('chat'); + + // Checking if onChatPanelStatusChanged has been received + await utils.waitForElement('#onChatPanelStatusChanged-false'); + expect(await utils.isPresent('#onChatPanelStatusChanged-false')).to.be.true; + }); + + it('should receive the onParticipantsPanelStatusChanged event', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + + await utils.togglePanel('participants'); + + // Checking if onParticipantsPanelStatusChanged has been received + await utils.waitForElement('#onParticipantsPanelStatusChanged-true'); + expect(await utils.isPresent('#onParticipantsPanelStatusChanged-true')).to.be.true; + + await utils.togglePanel('participants'); + + // Checking if onParticipantsPanelStatusChanged has been received + await utils.waitForElement('#onParticipantsPanelStatusChanged-false'); + expect(await utils.isPresent('#onParticipantsPanelStatusChanged-false')).to.be.true; + }); + + it('should receive the onActivitiesPanelStatusChanged event', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + await utils.checkToolbarIsPresent(); + + await utils.togglePanel('activities'); + + // Checking if onActivitiesPanelStatusChanged has been received + await utils.waitForElement('#onActivitiesPanelStatusChanged-true'); + expect(await utils.isPresent('#onActivitiesPanelStatusChanged-true')).to.be.true; + + await utils.togglePanel('activities'); + + // Checking if onActivitiesPanelStatusChanged has been received + await utils.waitForElement('#onActivitiesPanelStatusChanged-false'); + expect(await utils.isPresent('#onActivitiesPanelStatusChanged-false')).to.be.true; + }); + + it('should receive the onSettingsPanelStatusChanged event', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + await utils.checkToolbarIsPresent(); + + await utils.togglePanel('settings'); + + // Checking if onSettingsPanelStatusChanged has been received + await utils.waitForElement('#onSettingsPanelStatusChanged-true'); + expect(await utils.isPresent('#onSettingsPanelStatusChanged-true')).to.be.true; + + await utils.togglePanel('settings'); + + // Checking if onSettingsPanelStatusChanged has been received + await utils.waitForElement('#onSettingsPanelStatusChanged-false'); + expect(await utils.isPresent('#onSettingsPanelStatusChanged-false')).to.be.true; + }); + + it('should receive the onRecordingStartRequested event when clicking toolbar button', async () => { + const roomName = 'recordingToolbarEvent'; + await browser.get(`${url}&prejoin=false&roomName=${roomName}`); + + await utils.checkSessionIsPresent(); + await utils.checkToolbarIsPresent(); + + await utils.toggleRecordingFromToolbar(); + + // Checking if onRecordingStartRequested has been received + await utils.waitForElement(`#onRecordingStartRequested-${roomName}`); + expect(await utils.isPresent(`#onRecordingStartRequested-${roomName}`)).to.be.true; + }); + + xit('should receive the onRecordingStopRequested event when clicking toolbar button', async () => {}); + + xit('should receive the onBroadcastingStopRequested event when clicking toolbar button', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + await utils.checkToolbarIsPresent(); + + await utils.toggleToolbarMoreOptions(); + + await utils.waitForElement('#broadcasting-btn'); + await utils.clickOn('#broadcasting-btn'); + + await browser.sleep(500); + + await utils.waitForElement('.sidenav-menu'); + await utils.waitForElement('#activities-container'); + + await utils.waitForElement('#broadcasting-url-input'); + const input = await utils.waitForElement('#broadcast-url-input'); + await input.sendKeys('BroadcastUrl'); + await utils.clickOn('#broadcasting-btn'); + + // Open more options menu + await utils.toggleToolbarMoreOptions(); + + await utils.waitForElement('#broadcasting-btn'); + await utils.clickOn('#broadcasting-btn'); + + // Checking if onBroadcastingStopRequested has been received + await utils.waitForElement('#onBroadcastingStopRequested'); + expect(await utils.isPresent('#onBroadcastingStopRequested')).to.be.true; + }); + + it('should receive the onRecordingStartRequested when clicking from activities panel', async () => { + const roomName = 'recordingActivitiesEvent'; + await browser.get(`${url}&prejoin=false&roomName=${roomName}`); + + await utils.checkSessionIsPresent(); + await utils.checkToolbarIsPresent(); + + await utils.togglePanel('activities'); + + await browser.sleep(1000); + + // Open recording + await utils.waitForElement('ov-recording-activity'); + await utils.clickOn('ov-recording-activity'); + + await browser.sleep(1000); + + // Clicking to recording button + await utils.waitForElement('#start-recording-btn'); + await utils.clickOn('#start-recording-btn'); + + // Checking if onRecordingStartRequested has been received + await utils.waitForElement(`#onRecordingStartRequested-${roomName}`); + expect(await utils.isPresent(`#onRecordingStartRequested-${roomName}`)).to.be.true; + }); + + xit('should receive the onRecordingStopRequested when clicking from activities panel', async () => {}); + + xit('should receive the onRecordingDeleteRequested event', async () => { + let element; + const roomName = 'deleteRecordingEvent'; + await browser.get(`${url}&prejoin=false&roomName=${roomName}&fakeRecordings=true`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + + // Clicking to activities button + const activitiesButton = await utils.waitForElement('#activities-panel-btn'); + expect(await utils.isPresent('#activities-panel-btn')).to.be.true; + await activitiesButton.click(); + + await browser.sleep(1500); + // Open recording + element = await utils.waitForElement('ov-recording-activity'); + await element.click(); + + await browser.sleep(1500); + + // Delete event + element = await utils.waitForElement('#delete-recording-btn'); + expect(await utils.isPresent('#delete-recording-btn')).to.be.true; + await element.click(); + + element = await utils.waitForElement('#delete-recording-confirm-btn'); + expect(await utils.isPresent('#delete-recording-confirm-btn')).to.be.true; + await element.click(); + + await utils.waitForElement(`#onRecordingDeleteRequested-${roomName}-fakeRecording`); + expect(await utils.isPresent(`#onRecordingDeleteRequested-${roomName}-fakeRecording`)).to.be.true; + }); + + it('should receive the onBroadcastingStartRequested event when clicking from panel', async () => { + const roomName = 'broadcastingStartEvent'; + const broadcastUrl = 'BroadcastUrl'; + await browser.get(`${url}&prejoin=false&roomName=${roomName}`); + + await utils.checkSessionIsPresent(); + await utils.checkToolbarIsPresent(); + + await utils.togglePanel('activities'); + + await browser.sleep(1000); + await utils.waitForElement('#broadcasting-activity'); + await utils.clickOn('#broadcasting-activity'); + + await browser.sleep(1000); + + const button = await utils.waitForElement('#broadcasting-btn'); + expect(await button.isEnabled()).to.be.false; + + const input = await utils.waitForElement('#broadcast-url-input'); + await input.sendKeys(broadcastUrl); + + await utils.clickOn('#broadcasting-btn'); + + // Checking if onBroadcastingStartRequested has been received + await utils.waitForElement(`#onBroadcastingStartRequested-${roomName}-${broadcastUrl}`); + expect(await utils.isPresent(`#onBroadcastingStartRequested-${roomName}-${broadcastUrl}`)).to.be.true; + }); + + xit('should receive the onBroadcastingStopRequested event when clicking from panel', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + await utils.checkToolbarIsPresent(); + + // Open activities panel + await utils.togglePanel('activities'); + + await utils.waitForElement('#broadcasting-activity'); + await utils.clickOn('#broadcasting-activity'); + + const button = await utils.waitForElement('#broadcasting-btn'); + expect(await button.isEnabled()).to.be.false; + + const input = await utils.waitForElement('#broadcast-url-input'); + await input.sendKeys('BroadcastUrl'); + + await utils.clickOn('#broadcasting-btn'); + + expect(await utils.isPresent('#broadcasting-tag')).to.be.true; + + await utils.clickOn('#stop-broadcasting-btn'); + + // Checking if onBroadcastingStopRequested has been received + await utils.waitForElement('#onBroadcastingStopRequested'); + expect(await utils.isPresent('#onBroadcastingStopRequested')).to.be.true; + expect(await utils.isPresent('#broadcasting-tag')).to.be.false; + }); + + xit('should receive the onBroadcastingStopRequested event when clicking from toolbar', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + await utils.checkToolbarIsPresent(); + + // Open more options menu + await utils.toggleToolbarMoreOptions(); + await utils.waitForElement('#broadcasting-btn'); + await utils.clickOn('#broadcasting-btn'); + + await browser.sleep(500); + + // Checking if onBroadcastingStopRequested has been received + await utils.waitForElement('#onBroadcastingStopRequested'); + expect(await utils.isPresent('#onBroadcastingStopRequested')).to.be.true; + expect(await utils.isPresent('#broadcasting-tag')).to.be.false; + }); + + it('should receive the onRoomCreated event', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + + await utils.waitForElement('#onRoomCreated'); + expect(await utils.isPresent('#onRoomCreated')).to.be.true; + + expect(await utils.isPresent('#onReadyToJoin')).to.be.false; + }); + + // * PUBLISHER EVENTS + + it('should receive onParticipantCreated event from LOCAL participant', async () => { + const participantName = 'TEST_USER'; + await browser.get(`${url}&participantName=${participantName}&prejoin=false`); + await utils.waitForElement(`#${participantName}-onParticipantCreated`); + expect(await utils.isPresent(`#${participantName}-onParticipantCreated`)).to.be.true; + }); + + // * ROOM EVENTS + + it('should receive roomDisconnected event from LOCAL participant', async () => { + const participantName = 'TEST_USER'; + let element; + await browser.get(`${url}&prejoin=false&participantName=${participantName}`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + + // Checking if leave button is not present + element = await utils.waitForElement('#leave-btn'); + await element.click(); + + await utils.waitForElement(`#roomDisconnected`); + expect(await utils.isPresent(`#roomDisconnected`)).to.be.true; + }); +}); diff --git a/openvidu-components-angular/e2e/webcomponent-e2e/media-devices.test.ts b/openvidu-components-angular/e2e/webcomponent-e2e/media-devices.test.ts new file mode 100644 index 00000000..c7583ebb --- /dev/null +++ b/openvidu-components-angular/e2e/webcomponent-e2e/media-devices.test.ts @@ -0,0 +1,219 @@ +import { expect } from 'chai'; +import { Builder, WebDriver } from 'selenium-webdriver'; +import { OPENVIDU_CALL_SERVER } from '../config'; +import { getBrowserOptionsWithoutDevices, WebComponentConfig } from '../selenium.conf'; +import { OpenViduComponentsPO } from '../utils.po.test'; + +const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`; + +describe('Testing replace track with emulated devices', () => { + let browser: WebDriver; + let utils: OpenViduComponentsPO; + async function createChromeBrowser(): Promise { + return await new Builder() + .forBrowser(WebComponentConfig.browserName) + .withCapabilities(WebComponentConfig.browserCapabilities) + .setChromeOptions(WebComponentConfig.browserOptions) + .usingServer(WebComponentConfig.seleniumAddress) + .build(); + } + + beforeEach(async () => { + browser = await createChromeBrowser(); + utils = new OpenViduComponentsPO(browser); + }); + + afterEach(async () => { + // console.log('data:image/png;base64,' + await browser.takeScreenshot()); + await browser.quit(); + }); + + it('should replace the video track in prejoin page', async () => { + const script = 'return document.getElementsByTagName("video")[0].srcObject.getVideoTracks()[0].label;'; + + await browser.get(`${url}&fakeDevices=true`); + + let videoDevices = await utils.waitForElement('#video-devices-form'); + + await videoDevices.click(); + + let element = await utils.waitForElement('#option-custom_fake_video_1'); + + await element.click(); + + let videoLabel; + + await browser.sleep(1000); + videoLabel = await browser.executeScript(script); + expect(videoLabel).to.be.equal('custom_fake_video_1'); + + await videoDevices.click(); + + element = await utils.waitForElement('#option-fake_device_0'); + await element.click(); + + await browser.sleep(1000); + videoLabel = await browser.executeScript(script); + expect(videoLabel).to.be.equal('fake_device_0'); + }); + + it('should replace the video track in videoconference page', async () => { + const script = 'return document.getElementsByTagName("video")[0].srcObject.getVideoTracks()[0].label;'; + + await browser.get(`${url}&prejoin=false&fakeDevices=true`); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + await utils.togglePanel('settings'); + + await utils.waitForElement('.settings-container'); + expect(await utils.isPresent('.settings-container')).to.be.true; + + await utils.clickOn('#video-opt'); + expect(await utils.isPresent('ov-video-devices-select')).to.be.true; + + let videoDevices = await utils.waitForElement('#video-devices-form'); + + await videoDevices.click(); + + let element = await utils.waitForElement('#option-custom_fake_video_1'); + + await element.click(); + + let videoLabel; + await browser.sleep(1000); + videoLabel = await browser.executeScript(script); + expect(videoLabel).to.be.equal('custom_fake_video_1'); + + await videoDevices.click(); + + element = await utils.waitForElement('#option-fake_device_0'); + await element.click(); + + await browser.sleep(1000); + videoLabel = await browser.executeScript(script); + expect(videoLabel).to.be.equal('fake_device_0'); + }); + + // TODO: Uncommented when Livekit allows to replace the screen track + // it('should replace the screen track', async () => { + // const script = 'return document.getElementsByClassName("OV_video-element screen-type")[0].srcObject.getVideoTracks()[0].label;'; + + // await browser.get(`${url}&prejoin=false&fakeDevices=true`); + + // await utils.checkLayoutPresent(); + // await utils.checkToolbarIsPresent(); + + // await utils.clickOn('#screenshare-btn'); + + // await browser.sleep(500); + + // let screenLabel = await browser.executeScript(script); + // expect(screenLabel).not.equal('custom_fake_screen'); + + // await utils.clickOn('#video-settings-btn-SCREEN'); + // await browser.sleep(500); + + // await utils.waitForElement('.video-settings-menu'); + // const replaceBtn = await utils.waitForElement('#replace-screen-button'); + // await replaceBtn.sendKeys(Key.ENTER); + + // await browser.sleep(1000); + // screenLabel = await browser.executeScript(script); + // expect(screenLabel).to.be.equal('custom_fake_screen'); + // }); +}); + +describe('Testing WITHOUT MEDIA DEVICES permissions', () => { + let browser: WebDriver; + let utils: OpenViduComponentsPO; + async function createChromeBrowser(): Promise { + return await new Builder() + .forBrowser(WebComponentConfig.browserName) + .withCapabilities(WebComponentConfig.browserCapabilities) + .setChromeOptions(getBrowserOptionsWithoutDevices()) + .usingServer(WebComponentConfig.seleniumAddress) + .build(); + } + + beforeEach(async () => { + browser = await createChromeBrowser(); + utils = new OpenViduComponentsPO(browser); + }); + + afterEach(async () => { + await browser.quit(); + }); + + it('should be able to ACCESS to PREJOIN page', async () => { + await browser.get(`${url}`); + + await utils.checkPrejoinIsPresent(); + + let button = await utils.waitForElement('#camera-button'); + expect(await button.isEnabled()).to.be.false; + + button = await utils.waitForElement('#microphone-button'); + expect(await button.isEnabled()).to.be.false; + }); + + it('should be able to ACCESS to ROOM page', async () => { + await browser.get(`${url}`); + + await utils.checkPrejoinIsPresent(); + + await utils.clickOn('#join-button'); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + + let button = await utils.waitForElement('#camera-btn'); + expect(await button.isEnabled()).to.be.false; + + button = await utils.waitForElement('#mic-btn'); + expect(await button.isEnabled()).to.be.false; + }); + + it('should be able to ACCESS to ROOM page without prejoin', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + + let button = await utils.waitForElement('#camera-btn'); + expect(await button.isEnabled()).to.be.false; + + button = await utils.waitForElement('#mic-btn'); + expect(await button.isEnabled()).to.be.false; + }); + + it('should the settings buttons be disabled', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkToolbarIsPresent(); + + // Open more options menu + await utils.togglePanel('settings'); + await browser.sleep(500); + + await utils.waitForElement('.settings-container'); + expect(await utils.isPresent('.settings-container')).to.be.true; + + await utils.clickOn('#video-opt'); + expect(await utils.isPresent('ov-video-devices-select')).to.be.true; + + let button = await utils.waitForElement('#camera-button'); + expect(await button.isEnabled()).to.be.false; + + await utils.clickOn('#audio-opt'); + expect(await utils.isPresent('ov-audio-devices-select')).to.be.true; + + button = await utils.waitForElement('#microphone-button'); + expect(await button.isEnabled()).to.be.false; + }); +}); diff --git a/openvidu-components-angular/e2e/webcomponent-e2e/panels.test.ts b/openvidu-components-angular/e2e/webcomponent-e2e/panels.test.ts new file mode 100644 index 00000000..0d03493d --- /dev/null +++ b/openvidu-components-angular/e2e/webcomponent-e2e/panels.test.ts @@ -0,0 +1,225 @@ +import { expect } from 'chai'; +import { Builder, Key, WebDriver } from 'selenium-webdriver'; +import { OPENVIDU_CALL_SERVER } from '../config'; +import { getBrowserOptionsWithoutDevices, WebComponentConfig } from '../selenium.conf'; +import { OpenViduComponentsPO } from '../utils.po.test'; + +const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`; + +describe('Testing panels', () => { + let browser: WebDriver; + let utils: OpenViduComponentsPO; + + async function createChromeBrowser(): Promise { + return await new Builder() + .forBrowser(WebComponentConfig.browserName) + .withCapabilities(WebComponentConfig.browserCapabilities) + .setChromeOptions(WebComponentConfig.browserOptions) + .usingServer(WebComponentConfig.seleniumAddress) + .build(); + } + + beforeEach(async () => { + browser = await createChromeBrowser(); + utils = new OpenViduComponentsPO(browser); + }); + + afterEach(async () => { + await browser.quit(); + }); + + /** + * TODO + * It only works with OpenVidu PRO because this is a PRO feature + */ + // it('should toggle BACKGROUND panel on prejoin page when VIDEO is MUTED', async () => { + // let element; + // await browser.get(`${url}`); + // element = await utils.waitForElement('#pre-join-container'); + // expect(await utils.isPresent('#pre-join-container')).to.be.true; + + // const backgroundButton = await utils.waitForElement('#background-effects-btn'); + // expect(await utils.isPresent('#background-effects-btn')).to.be.true; + // expect(await backgroundButton.isEnabled()).to.be.true; + // await backgroundButton.click(); + // await browser.sleep(500); + + // await utils.waitForElement('#background-effects-container'); + // expect(await utils.isPresent('#background-effects-container')).to.be.true; + + // element = await utils.waitForElement('#camera-button'); + // expect(await utils.isPresent('#camera-button')).to.be.true; + // expect(await element.isEnabled()).to.be.true; + // await element.click(); + + // await browser.sleep(500); + // element = await utils.waitForElement('#video-poster'); + // expect(await utils.isPresent('#video-poster')).to.be.true; + + // expect(await backgroundButton.isDisplayed()).to.be.true; + // expect(await backgroundButton.isEnabled()).to.be.false; + + // expect(await utils.isPresent('#background-effects-container')).to.be.false; + // }); + + it('should toggle CHAT panel', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkLayoutPresent(); + + const chatButton = await utils.waitForElement('#chat-panel-btn'); + await chatButton.click(); + + await utils.waitForElement('.sidenav-menu'); + await utils.waitForElement('.input-container'); + expect(await utils.isPresent('.input-container')).to.be.true; + + await utils.waitForElement('.messages-container'); + expect(await utils.isPresent('.messages-container')).to.be.true; + + await chatButton.click(); + + expect(await utils.isPresent('.input-container')).to.be.false; + expect(await utils.isPresent('.messages-container')).to.be.false; + }); + + it('should toggle PARTICIPANTS panel', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkLayoutPresent(); + + const participantBtn = await utils.waitForElement('#participants-panel-btn'); + await participantBtn.click(); + + await utils.waitForElement('.sidenav-menu'); + + await utils.waitForElement('.local-participant-container'); + expect(await utils.isPresent('.local-participant-container')).to.be.true; + + await utils.waitForElement('ov-participant-panel-item'); + expect(await utils.isPresent('ov-participant-panel-item')).to.be.true; + + await participantBtn.click(); + + expect(await utils.isPresent('.local-participant-container')).to.be.false; + expect(await utils.isPresent('ov-participant-panel-item')).to.be.false; + }); + + it('should toggle ACTIVITIES panel', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkLayoutPresent(); + + // Get activities button and click into it + const activitiesBtn = await utils.waitForElement('#activities-panel-btn'); + await activitiesBtn.click(); + + await utils.waitForElement('.sidenav-menu'); + await utils.waitForElement('#activities-container'); + expect(await utils.isPresent('#activities-container')).to.be.true; + + await utils.waitForElement('#recording-activity'); + expect(await utils.isPresent('#recording-activity')).to.be.true; + await activitiesBtn.click(); + + expect(await utils.isPresent('#activities-container')).to.be.false; + expect(await utils.isPresent('#recording-activity')).to.be.false; + }); + + it('should toggle SETTINGS panel', async () => { + let element; + await browser.get(`${url}&prejoin=false`); + + await utils.checkLayoutPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + await utils.togglePanel('settings'); + + element = await utils.waitForElement('.sidenav-menu'); + expect(await utils.isPresent('#default-settings-panel')).to.be.true; + }); + + it('should switching between PARTICIPANTS and CHAT panels', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkSessionIsPresent(); + + await utils.checkToolbarIsPresent(); + + // Open chat panel + const chatButton = await utils.waitForElement('#chat-panel-btn'); + await chatButton.click(); + + await utils.waitForElement('.sidenav-menu'); + expect(await utils.isPresent('.sidenav-menu')).to.be.true; + + await utils.waitForElement('.input-container'); + expect(await utils.isPresent('.input-container')).to.be.true; + + expect(await utils.isPresent('.messages-container')).to.be.true; + + // Open participants panel + const participantBtn = await utils.waitForElement('#participants-panel-btn'); + await participantBtn.click(); + + await utils.waitForElement('.sidenav-menu'); + + expect(await utils.isPresent('.local-participant-container')).to.be.true; + + expect(await utils.isPresent('ov-participant-panel-item')).to.be.true; + + // Switch to chat panel + await chatButton.click(); + + await utils.waitForElement('.sidenav-menu'); + + expect(await utils.isPresent('.input-container')).to.be.true; + + expect(await utils.isPresent('.messages-container')).to.be.true; + + expect(await utils.isPresent('.local-participant-container')).to.be.false; + + expect(await utils.isPresent('ov-participant-panel-item')).to.be.false; + + // Close chat panel + await chatButton.click(); + expect(await utils.getNumberOfElements('.input-container')).equals(0); + expect(await utils.isPresent('messages-container')).to.be.false; + }); + + it('should switching between sections in SETTINGS PANEL', async () => { + let element; + await browser.get(`${url}&prejoin=false`); + + await utils.checkToolbarIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + // Open more options menu + await utils.togglePanel('settings'); + + await utils.waitForElement('.sidenav-menu'); + expect(await utils.isPresent('.sidenav-menu')).to.be.true; + + // Check if general section is shown + element = await utils.waitForElement('#general-opt'); + await element.click(); + + expect(await utils.isPresent('ov-participant-name-input')).to.be.true; + + // Check if video section is shown + element = await utils.waitForElement('#video-opt'); + await element.click(); + + expect(await utils.isPresent('ov-video-devices-select')).to.be.true; + + // Check if audio section is shown + element = await utils.waitForElement('#audio-opt'); + await element.click(); + + expect(await utils.isPresent('ov-audio-devices-select')).to.be.true; + }); +}); diff --git a/openvidu-components-angular/e2e/webcomponent-e2e/screensharing.test.ts b/openvidu-components-angular/e2e/webcomponent-e2e/screensharing.test.ts new file mode 100644 index 00000000..ecdc2f1c --- /dev/null +++ b/openvidu-components-angular/e2e/webcomponent-e2e/screensharing.test.ts @@ -0,0 +1,326 @@ +import { expect } from 'chai'; +import { Builder, WebDriver } from 'selenium-webdriver'; +import { OPENVIDU_CALL_SERVER } from '../config'; +import { WebComponentConfig } from '../selenium.conf'; +import { OpenViduComponentsPO } from '../utils.po.test'; + +const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`; + +describe('Testing screenshare features', () => { + let browser: WebDriver; + let utils: OpenViduComponentsPO; + async function createChromeBrowser(): Promise { + return await new Builder() + .forBrowser(WebComponentConfig.browserName) + .withCapabilities(WebComponentConfig.browserCapabilities) + .setChromeOptions(WebComponentConfig.browserOptions) + .usingServer(WebComponentConfig.seleniumAddress) + .build(); + } + + beforeEach(async () => { + browser = await createChromeBrowser(); + utils = new OpenViduComponentsPO(browser); + }); + + afterEach(async () => { + await browser.quit(); + }); + + it('should toggle screensharing twice', async () => { + await browser.get(`${url}&prejoin=false`); + await utils.checkLayoutPresent(); + + // Clicking to screensharing button + await utils.waitForElement('#screenshare-btn'); + await utils.clickOn('#screenshare-btn'); + + await browser.sleep(500); + await utils.waitForElement('.OV_big'); + expect(await utils.getNumberOfElements('video')).equals(2); + expect(await utils.getNumberOfElements('.OV_stream.speaking')).equals(1); + + // Clicking to screensharing button + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + await utils.waitForElement('#screenshare-menu'); + await utils.clickOn('#disable-screen-button'); + await browser.sleep(500); + + expect(await utils.getNumberOfElements('video')).equals(1); + + // toggle screenshare again + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + + await utils.waitForElement('.OV_big'); + expect(await utils.getNumberOfElements('video')).equals(2); + + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + await utils.waitForElement('#screenshare-menu'); + await utils.clickOn('#disable-screen-button'); + await browser.sleep(500); + + expect(await utils.getNumberOfElements('video')).equals(1); + }); + + it('should show screen and muted camera', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkLayoutPresent(); + + await utils.waitForElement('#camera-btn'); + await utils.clickOn('#camera-btn'); + + // Clicking to screensharing button + const screenshareButton = await utils.waitForElement('#screenshare-btn'); + expect(await screenshareButton.isDisplayed()).to.be.true; + await screenshareButton.click(); + + await browser.sleep(500); + await utils.waitForElement('.OV_big'); + expect(await utils.getNumberOfElements('video')).equals(2); + + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + await utils.waitForElement('#screenshare-menu'); + await utils.clickOn('#disable-screen-button'); + await browser.sleep(500); + + expect(await utils.getNumberOfElements('video')).equals(1); + }); + + it('should screensharing with PINNED video', async () => { + await browser.get(`${url}&prejoin=false`); + await utils.checkLayoutPresent(); + + // Clicking to screensharing button + const screenshareButton = await utils.waitForElement('#screenshare-btn'); + expect(await screenshareButton.isDisplayed()).to.be.true; + await screenshareButton.click(); + + await utils.waitForElement('.OV_big'); + expect(await utils.getNumberOfElements('.OV_big')).equals(1); + }); + + it('should screensharing with PINNED video and replace the existing one', async () => { + const roomName = 'screensharingE2E'; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`; + await browser.get(fixedUrl); + await utils.checkLayoutPresent(); + + // Clicking to screensharing button + await utils.waitForElement('#screenshare-btn'); + await utils.clickOn('#screenshare-btn'); + await utils.waitForElement('.OV_big'); + expect(await utils.getNumberOfElements('.OV_big')).equals(1); + + // Starting new browser for adding the second participant + const newTabScript = `window.open("${fixedUrl}")`; + await browser.executeScript(newTabScript); + const tabs = await browser.getAllWindowHandles(); + await browser.switchTo().window(tabs[1]); + + await utils.checkLayoutPresent(); + + // Clicking to screensharing button + await utils.waitForElement('#screenshare-btn'); + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + expect(await utils.getNumberOfElements('video')).equals(4); + await utils.waitForElement('.OV_big'); + expect(await utils.getNumberOfElements('.OV_big')).equals(1); + + // Go to first tab + await browser.switchTo().window(tabs[0]); + await browser.sleep(500); + expect(await utils.getNumberOfElements('video')).equals(4); + await utils.waitForElement('.OV_big'); + expect(await utils.getNumberOfElements('.OV_big')).equals(1); + }); + + it('should disabled a screensharing and pinned the previous one', async () => { + const roomName = 'screensharingtwoE2E'; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`; + await browser.get(fixedUrl); + await utils.checkLayoutPresent(); + + // Clicking to screensharing button + await utils.waitForElement('#screenshare-btn'); + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + await utils.waitForElement('.OV_big'); + expect(await utils.getNumberOfElements('.OV_big')).equals(1); + + // Starting new browser for adding the second participant + const tabs = await utils.openTab(fixedUrl); + await browser.switchTo().window(tabs[1]); + + await utils.checkLayoutPresent(); + + // Clicking to screensharing button + await utils.waitForElement('#screenshare-btn'); + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + expect(await utils.getNumberOfElements('video')).equals(4); + await utils.waitForElement('.OV_big'); + expect(await utils.getNumberOfElements('.OV_big')).equals(1); + // Disable screensharing + await utils.clickOn('#screenshare-btn'); + await utils.waitForElement('#screenshare-menu'); + await utils.clickOn('#disable-screen-button'); + await browser.sleep(500); + expect(await utils.getNumberOfElements('video')).equals(3); + await utils.waitForElement('.OV_big'); + expect(await utils.getNumberOfElements('.OV_big')).equals(1); + + // Go to first tab + await browser.switchTo().window(tabs[0]); + await browser.sleep(500); + expect(await utils.getNumberOfElements('video')).equals(3); + await utils.waitForElement('.OV_big'); + expect(await utils.getNumberOfElements('.OV_big')).equals(1); + }); + + // it('should screensharing with audio muted', async () => { + // let isAudioEnabled; + // const getAudioScript = (className: string) => { + // return `return document.getElementsByClassName('${className}')[0].srcObject.getAudioTracks()[0].enabled;`; + // }; + // await browser.get(`${url}&prejoin=false`); + + // await utils.checkLayoutPresent(); + + // const micButton = await utils.waitForElement('#mic-btn'); + // await micButton.click(); + + // // Clicking to screensharing button + // const screenshareButton = await utils.waitForElement('#screenshare-btn'); + // expect(await utils.isPresent('#screenshare-btn')).to.be.true; + // await screenshareButton.click(); + + // await utils.waitForElement('.screen-type'); + // expect(await utils.getNumberOfElements('video')).equals(2); + + // isAudioEnabled = await browser.executeScript(getAudioScript('screen-type')); + // expect(isAudioEnabled).to.be.false; + + // await utils.waitForElement('#status-mic'); + // expect(await utils.getNumberOfElements('#status-mic')).equals(2); + + // // Clicking to screensharing button + // await screenshareButton.click(); + // expect(await utils.getNumberOfElements('video')).equals(1); + + // }); + + // it('should show and hide CAMERA stream when muting video with screensharing', async () => { + // await browser.get(`${url}&prejoin=false`); + + // await utils.checkLayoutPresent(); + + // // Clicking to screensharing button + // const screenshareButton = await utils.waitForElement('#screenshare-btn'); + // expect(await screenshareButton.isDisplayed()).to.be.true; + // await screenshareButton.click(); + + // await utils.waitForElement('.OV_big'); + // expect(await utils.getNumberOfElements('video')).equals(2); + + // const muteVideoButton = await utils.waitForElement('#camera-btn'); + // await muteVideoButton.click(); + + // expect(await utils.getNumberOfElements('video')).equals(1); + // }); + + // it('should screenshare has audio active when camera is muted', async () => { + // let isAudioEnabled; + // const audioEnableScript = 'return document.getElementsByTagName("video")[0].srcObject.getAudioTracks()[0].enabled;'; + + // await browser.get(`${url}&prejoin=false`); + + // await utils.checkLayoutPresent(); + + // // Clicking to screensharing button + // const screenshareButton = await utils.waitForElement('#screenshare-btn'); + // expect(await utils.isPresent('#screenshare-btn')).to.be.true; + // await screenshareButton.click(); + + // await utils.waitForElement('.OV_big'); + // expect(await utils.getNumberOfElements('video')).equals(2); + // expect(await utils.getNumberOfElements('#status-mic')).equals(1); + + // // Muting camera video + // const muteVideoButton = await utils.waitForElement('#camera-btn'); + // await muteVideoButton.click(); + + // expect(await utils.getNumberOfElements('video')).equals(1); + + // await browser.sleep(500); + // expect(await utils.isPresent('#status-mic')).to.be.false; + + // // Checking if audio is muted after join the room + // isAudioEnabled = await browser.executeScript(audioEnableScript); + // expect(isAudioEnabled).to.be.true; + + // // Unmuting camera + // await muteVideoButton.click(); + // await browser.sleep(1000); + + // await utils.waitForElement('.camera-type'); + // expect(await utils.getNumberOfElements('video')).equals(2); + // expect(await utils.getNumberOfElements('#status-mic')).equals(1); + // }); + + // it('should camera come back with audio muted when screensharing', async () => { + // let element, isAudioEnabled; + + // const getAudioScript = (className: string) => { + // return `return document.getElementsByClassName('${className}')[0].srcObject.getAudioTracks()[0].enabled;`; + // }; + + // await browser.get(`${url}&prejoin=false`); + + // await utils.checkLayoutPresent(); + + // // Clicking to screensharing button + // const screenshareButton = await utils.waitForElement('#screenshare-btn'); + // await screenshareButton.click(); + + // await utils.waitForElement('.screen-type'); + // expect(await utils.getNumberOfElements('video')).equals(2); + // expect(await utils.getNumberOfElements('#status-mic')).equals(1); + + // // Mute camera + // const muteVideoButton = await utils.waitForElement('#camera-btn'); + // await muteVideoButton.click(); + + // expect(await utils.getNumberOfElements('video')).equals(1); + // expect(await utils.isPresent('#status-mic')).to.be.false; + + // // Checking if audio is muted after join the room + // isAudioEnabled = await browser.executeScript(getAudioScript('screen-type')); + // expect(isAudioEnabled).to.be.true; + + // // Mute audio + // const muteAudioButton = await utils.waitForElement('#mic-btn'); + // await muteAudioButton.click(); + + // await utils.waitForElement('#status-mic'); + // expect(await utils.getNumberOfElements('#status-mic')).equals(1); + + // isAudioEnabled = await browser.executeScript(getAudioScript('screen-type')); + // expect(isAudioEnabled).to.be.false; + + // // Unmute camera + // await muteVideoButton.click(); + + // await utils.waitForElement('.camera-type'); + // expect(await utils.getNumberOfElements('video')).equals(2); + // expect(await utils.getNumberOfElements('#status-mic')).equals(2); + + // isAudioEnabled = await browser.executeScript(getAudioScript('camera-type')); + // expect(isAudioEnabled).to.be.false; + // }); +}); diff --git a/openvidu-components-angular/e2e/webcomponent-e2e/stream.test.ts b/openvidu-components-angular/e2e/webcomponent-e2e/stream.test.ts new file mode 100644 index 00000000..61d5fde5 --- /dev/null +++ b/openvidu-components-angular/e2e/webcomponent-e2e/stream.test.ts @@ -0,0 +1,863 @@ +import { expect } from 'chai'; +import { Builder, ILocation, IRectangle, ISize, WebDriver } from 'selenium-webdriver'; +import { OPENVIDU_CALL_SERVER } from '../config'; +import { WebComponentConfig } from '../selenium.conf'; +import { OpenViduComponentsPO } from '../utils.po.test'; + +const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`; + +describe('Checking stream elements by disabling/enabling the media', () => { + let browser: WebDriver; + let utils: OpenViduComponentsPO; + async function createChromeBrowser(): Promise { + return await new Builder() + .forBrowser(WebComponentConfig.browserName) + .withCapabilities(WebComponentConfig.browserCapabilities) + .setChromeOptions(WebComponentConfig.browserOptions) + .usingServer(WebComponentConfig.seleniumAddress) + .build(); + } + + beforeEach(async () => { + browser = await createChromeBrowser(); + utils = new OpenViduComponentsPO(browser); + }); + + afterEach(async () => { + await browser.quit(); + }); + + it('should show ONE video element when a LOCAL participant joins with VIDEO and AUDIO MUTED', async () => { + await browser.get(`${url}&prejoin=true&videoEnabled=false&audioEnabled=false`); + + await utils.checkPrejoinIsPresent(); + await utils.clickOn('#join-button'); + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + + expect(await utils.getNumberOfElements('.OV_stream')).equal(1); + }); + + it('should show ONE video element when a LOCAL participant joins with AUDIO MUTED', async () => { + await browser.get(`${url}&prejoin=true&videoEnabled=true&audioEnabled=false`); + + await utils.checkPrejoinIsPresent(); + await utils.clickOn('#join-button'); + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + + expect(await utils.getNumberOfElements('.OV_stream')).equal(1); + }); + + it('should show a video element when a LOCAL participant joins with VIDEO MUTED', async () => { + await browser.get(`${url}&prejoin=true&videoEnabled=false&audioEnabled=true`); + + await utils.checkPrejoinIsPresent(); + await utils.clickOn('#join-button'); + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + + expect(await utils.getNumberOfElements('.OV_stream')).equal(1); + }); + + it('should show a video element when a LOCAL participant joins', async () => { + await browser.get(`${url}&prejoin=true&videoEnabled=true&audioEnabled=true`); + + await utils.checkPrejoinIsPresent(); + await utils.clickOn('#join-button'); + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + + expect(await utils.getNumberOfElements('.OV_stream')).equal(1); + }); + + it('should show a video element when a LOCAL participant shares its screen with VIDEO and AUDIO MUTED', async () => { + await browser.get(`${url}&prejoin=true&videoEnabled=false&audioEnabled=false`); + + await utils.checkPrejoinIsPresent(); + await utils.clickOn('#join-button'); + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + + expect(await utils.getNumberOfElements('.OV_stream')).equal(1); + + await utils.clickOn('#screenshare-btn'); + await browser.sleep(1000); + await utils.waitForElement('#local-element-screen_share'); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + await utils.waitForElement('#screenshare-menu'); + await utils.clickOn('#disable-screen-button'); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.OV_stream')).equal(1); + }); + + it('should show a video element when a LOCAL participant shares its screen with VIDEO MUTED', async () => { + await browser.get(`${url}&prejoin=true&videoEnabled=false&audioEnabled=true`); + + await utils.checkPrejoinIsPresent(); + await utils.clickOn('#join-button'); + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + + expect(await utils.getNumberOfElements('.OV_stream')).equal(1); + + await utils.clickOn('#screenshare-btn'); + await browser.sleep(1000); + await utils.waitForElement('#local-element-screen_share'); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + await utils.waitForElement('#screenshare-menu'); + await utils.clickOn('#disable-screen-button'); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.OV_stream')).equal(1); + }); + + it('should show a video element when a LOCAL participant shares its screen with AUDIO MUTED', async () => { + await browser.get(`${url}&prejoin=true&videoEnabled=true&audioEnabled=false`); + + await utils.checkPrejoinIsPresent(); + await utils.clickOn('#join-button'); + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + + expect(await utils.getNumberOfElements('.OV_stream')).equal(1); + + await utils.clickOn('#screenshare-btn'); + await browser.sleep(1000); + await utils.waitForElement('#local-element-screen_share'); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + await utils.waitForElement('#screenshare-menu'); + await utils.clickOn('#disable-screen-button'); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.OV_stream')).equal(1); + }); + + it('should show a video element when a LOCAL participant shares its screen', async () => { + await browser.get(`${url}&prejoin=true&videoEnabled=true&audioEnabled=true`); + + await utils.checkPrejoinIsPresent(); + await utils.clickOn('#join-button'); + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + + expect(await utils.getNumberOfElements('.OV_stream')).equal(1); + + await utils.clickOn('#screenshare-btn'); + await browser.sleep(1000); + await utils.waitForElement('#local-element-screen_share'); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + await utils.waitForElement('#screenshare-menu'); + await utils.clickOn('#disable-screen-button'); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.OV_stream')).equal(1); + }); + + /* ------------ Checking video elements with two participants ------------ */ + + it('should show TWO video elements when a REMOTE participant joins with VIDEO and AUDIO MUTED', async () => { + const roomName = `streams-${Date.now()}`; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false&audioEnabled=false`; + await browser.get(fixedUrl); + + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + + expect(await utils.getNumberOfElements('.OV_stream')).equal(1); + + const tabs = await utils.openTab(fixedUrl); + await browser.switchTo().window(tabs[0]); + + await browser.sleep(1000); + await utils.waitForElement('.OV_stream.remote'); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + + await browser.switchTo().window(tabs[1]); + await browser.sleep(1000); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + }); + + it('should show TWO video elements when a REMOTE participant joins with AUDIO MUTED', async () => { + const roomName = `streams-${Date.now()}`; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=true&audioEnabled=false`; + await browser.get(fixedUrl); + + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + + expect(await utils.getNumberOfElements('.OV_stream')).equal(1); + + const tabs = await utils.openTab(fixedUrl); + await browser.switchTo().window(tabs[0]); + + await browser.sleep(1000); + await utils.waitForElement('.OV_stream.remote'); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + + await browser.switchTo().window(tabs[1]); + await browser.sleep(1000); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + }); + + it('should show TWO video elements when a REMOTE participant joins with VIDEO MUTED', async () => { + const roomName = `streams-${Date.now()}`; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false`; + await browser.get(fixedUrl); + + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + + expect(await utils.getNumberOfElements('.OV_stream')).equal(1); + + const tabs = await utils.openTab(fixedUrl); + await browser.switchTo().window(tabs[0]); + + await browser.sleep(1000); + await utils.waitForElement('.OV_stream.remote'); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + await browser.switchTo().window(tabs[1]); + await browser.sleep(1000); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + }); + + it('should show TWO video elements when a REMOTE participant joins', async () => { + const roomName = `streams-${Date.now()}`; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=true&audioEnabled=true`; + await browser.get(fixedUrl); + + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + + expect(await utils.getNumberOfElements('.OV_stream')).equal(1); + + const tabs = await utils.openTab(fixedUrl); + await browser.switchTo().window(tabs[0]); + + await browser.sleep(1000); + await utils.waitForElement('.OV_stream.remote'); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + await browser.switchTo().window(tabs[1]); + await browser.sleep(1000); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + }); + + it('should show THREE video elements when a REMOTE participant shares its screen with AUDIO and VIDEO MUTED', async () => { + const roomName = `streams-${Date.now()}`; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false&audioEnabled=false`; + await browser.get(fixedUrl); + + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + + const tabs = await utils.openTab(fixedUrl); + await browser.switchTo().window(tabs[1]); + + await utils.waitForElement('.OV_stream.local'); + + await utils.clickOn('#screenshare-btn'); + await browser.sleep(1000); + await utils.waitForElement('#local-element-screen_share'); + expect(await utils.getNumberOfElements('.OV_stream')).equal(3); + + await browser.switchTo().window(tabs[0]); + await browser.sleep(1000); + expect(await utils.getNumberOfElements('.OV_stream')).equal(3); + + await browser.switchTo().window(tabs[1]); + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + await utils.waitForElement('#screenshare-menu'); + await utils.clickOn('#disable-screen-button'); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + + await browser.switchTo().window(tabs[0]); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + }); + + it('should show THREE video elements when a REMOTE participant shares its screen with VIDEO MUTED', async () => { + const roomName = `streams-${Date.now()}`; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false&audioEnabled=true`; + await browser.get(fixedUrl); + + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + + const tabs = await utils.openTab(fixedUrl); + await browser.switchTo().window(tabs[1]); + + await utils.waitForElement('.OV_stream.local'); + + await utils.clickOn('#screenshare-btn'); + await browser.sleep(1000); + await utils.waitForElement('#local-element-screen_share'); + expect(await utils.getNumberOfElements('.OV_stream')).equal(3); + + await browser.switchTo().window(tabs[0]); + await browser.sleep(1000); + expect(await utils.getNumberOfElements('.OV_stream')).equal(3); + + await browser.switchTo().window(tabs[1]); + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + await utils.waitForElement('#screenshare-menu'); + await utils.clickOn('#disable-screen-button'); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + + await browser.switchTo().window(tabs[0]); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + }); + + it('should show THREE video elements when a REMOTE participant shares its screen with AUDIO MUTED', async () => { + const roomName = `streams-${Date.now()}`; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=true&audioEnabled=false`; + await browser.get(fixedUrl); + + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + + const tabs = await utils.openTab(fixedUrl); + await browser.switchTo().window(tabs[1]); + + await utils.waitForElement('.OV_stream.local'); + + await utils.clickOn('#screenshare-btn'); + await browser.sleep(1000); + await utils.waitForElement('#local-element-screen_share'); + expect(await utils.getNumberOfElements('.OV_stream')).equal(3); + + await browser.switchTo().window(tabs[0]); + await browser.sleep(1000); + expect(await utils.getNumberOfElements('.OV_stream')).equal(3); + + await browser.switchTo().window(tabs[1]); + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + await utils.waitForElement('#screenshare-menu'); + await utils.clickOn('#disable-screen-button'); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + + await browser.switchTo().window(tabs[0]); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + }); + + it('should show THREE video elements when a REMOTE participant shares its screen', async () => { + const roomName = `streams-${Date.now()}`; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=true&audioEnabled=true`; + await browser.get(fixedUrl); + + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + + const tabs = await utils.openTab(fixedUrl); + await browser.switchTo().window(tabs[1]); + + await utils.waitForElement('.OV_stream.local'); + + await utils.clickOn('#screenshare-btn'); + await browser.sleep(1000); + await utils.waitForElement('#local-element-screen_share'); + expect(await utils.getNumberOfElements('.OV_stream')).equal(3); + + await browser.switchTo().window(tabs[0]); + await browser.sleep(1000); + expect(await utils.getNumberOfElements('.OV_stream')).equal(3); + + await browser.switchTo().window(tabs[1]); + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + await utils.waitForElement('#screenshare-menu'); + await utils.clickOn('#disable-screen-button'); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + + await browser.switchTo().window(tabs[0]); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.OV_stream')).equal(2); + }); + + it('should show FOUR video elements when a two participant shares theirs screen', async () => { + const roomName = `streams-${Date.now()}`; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false&audioEnabled=false`; + await browser.get(fixedUrl); + + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + + const tabs = await utils.openTab(fixedUrl); + await browser.switchTo().window(tabs[1]); + + await utils.waitForElement('.OV_stream.local'); + expect(await utils.getNumberOfElements('.OV_stream')).equal(3); + + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + await utils.waitForElement('#local-element-screen_share'); + expect(await utils.getNumberOfElements('.OV_stream')).equal(4); + + await browser.switchTo().window(tabs[0]); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.OV_stream')).equal(4); + await utils.clickOn('#camera-btn'); + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); + await utils.waitForElement('#screenshare-menu'); + await utils.clickOn('#disable-screen-button'); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.OV_stream')).equal(3); + + await browser.switchTo().window(tabs[1]); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.OV_stream')).equal(3); + }); +}); + +describe('Testing stream features', () => { + let browser: WebDriver; + let utils: OpenViduComponentsPO; + async function createChromeBrowser(): Promise { + return await new Builder() + .forBrowser(WebComponentConfig.browserName) + .withCapabilities(WebComponentConfig.browserCapabilities) + .setChromeOptions(WebComponentConfig.browserOptions) + .usingServer(WebComponentConfig.seleniumAddress) + .build(); + } + + beforeEach(async () => { + browser = await createChromeBrowser(); + utils = new OpenViduComponentsPO(browser); + }); + + afterEach(async () => { + await browser.quit(); + }); + + it('should show the PIN button over the LOCAL video', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + await utils.hoverOn('.OV_stream.local'); + await utils.waitForElement('#pin-btn'); + expect(await utils.isPresent('#pin-btn')).to.be.true; + }); + + it('should show the PIN button over the REMOTE video', async () => { + const roomName = 'pinE2E'; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`; + await browser.get(fixedUrl); + + await utils.checkLayoutPresent(); + + // Starting new browser for adding the second participant + const newTabScript = `window.open("${fixedUrl}")`; + await browser.executeScript(newTabScript); + const tabs = await browser.getAllWindowHandles(); + await browser.switchTo().window(tabs[1]); + + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + await utils.waitForElement('.OV_stream.remote'); + await utils.hoverOn('.OV_stream.remote'); + await utils.waitForElement('#pin-btn'); + expect(await utils.isPresent('#pin-btn')).to.be.true; + }); + + it('should show the SILENCE button ONLY over the REMOTE video', async () => { + const roomName = 'silenceE2E'; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`; + await browser.get(fixedUrl); + + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + await utils.hoverOn('.OV_stream.local'); + await browser.sleep(500); + + expect(await utils.getNumberOfElements('.OV_stream.local #silence-btn')).equals(0); + + // Starting new browser for adding the second participant + const newTabScript = `window.open("${fixedUrl}")`; + await browser.executeScript(newTabScript); + const tabs = await browser.getAllWindowHandles(); + await browser.switchTo().window(tabs[1]); + + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.remote'); + await utils.hoverOn('.OV_stream.remote'); + await utils.waitForElement('.OV_stream.remote #silence-btn'); + expect(await utils.isPresent('.OV_stream.remote #silence-btn')).to.be.true; + expect(await utils.getNumberOfElements('.OV_stream.remote #silence-btn')).equals(1); + + await utils.hoverOn('.OV_stream.local'); + await browser.sleep(500); + + expect(await utils.getNumberOfElements('.OV_stream.local #silence-btn')).equals(0); + }); + + it('should show the MINIMIZE button ONLY over the LOCAL video', async () => { + const roomName = 'minimizeE2E'; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`; + await browser.get(fixedUrl); + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + await utils.hoverOn('.OV_stream.local'); + await utils.waitForElement('#minimize-btn'); + expect(await utils.isPresent('#minimize-btn')).to.be.true; + + // Starting new browser for adding the second participant + const newTabScript = `window.open("${fixedUrl}")`; + await browser.executeScript(newTabScript); + const tabs = await browser.getAllWindowHandles(); + await browser.switchTo().window(tabs[1]); + + await utils.checkLayoutPresent(); + await utils.waitForElement('.OV_stream.local'); + await utils.hoverOn('.OV_stream.remote'); + expect(await utils.getNumberOfElements('#minimize-btn')).equals(0); + + await utils.hoverOn('.OV_stream.local'); + await utils.waitForElement('#minimize-btn'); + expect(await utils.isPresent('#minimize-btn')).to.be.true; + }); + + it('should minimize the LOCAL video', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkLayoutPresent(); + + const stream = await utils.waitForElement('.OV_stream.local'); + const streamProps: IRectangle = await stream.getRect(); + + await utils.hoverOn('.OV_stream.local'); + await utils.waitForElement('#minimize-btn'); + await utils.clickOn('#minimize-btn'); + await browser.sleep(900); + const minimizeStream = await utils.waitForElement('.OV_stream.local'); + const minimizeStreamProps: IRectangle = await minimizeStream.getRect(); + expect(streamProps.height).not.equals(minimizeStreamProps.height); + expect(streamProps.width).not.equals(minimizeStreamProps.width); + expect(minimizeStreamProps.x).lessThan(100); + expect(minimizeStreamProps.y).lessThan(100); + }); + + it('should MAXIMIZE the local video', async () => { + await browser.get(`${url}&prejoin=false&audioEnabled=false`); + const marginX = 5; + + await utils.checkLayoutPresent(); + await utils.subscribeToDropEvent(); + + await utils.waitForElement('.OV_stream.local'); + await utils.hoverOn('.OV_stream.local'); + await utils.waitForElement('#minimize-btn'); + await utils.clickOn('#minimize-btn'); + + await browser.sleep(500); + await utils.dragToRight(300, 300); + await browser.sleep(500); + + let stream = await utils.waitForElement('.OV_stream.local'); + let streamProps: IRectangle = await stream.getRect(); + expect(streamProps.x).equals(300 + marginX); + expect(streamProps.y).equals(300); + + await utils.hoverOn('.OV_stream.local'); + await utils.waitForElement('#minimize-btn'); + await utils.clickOn('#minimize-btn'); + await browser.sleep(1500); + + stream = await utils.waitForElement('.OV_stream.local'); + streamProps = await stream.getRect(); + expect(streamProps.x).lessThan(300 + marginX); + expect(streamProps.y).equals(1); //.OV_publisher element has 1px of padding + }); + + it('should be able to dragg the minimized LOCAL video', async () => { + await browser.get(`${url}&prejoin=false&audioEnabled=false`); + const marginX = 5; + + await utils.checkLayoutPresent(); + await utils.subscribeToDropEvent(); + + await utils.waitForElement('.OV_stream.local'); + await utils.hoverOn('.OV_stream.local'); + await utils.waitForElement('#minimize-btn'); + await utils.clickOn('#minimize-btn'); + + await browser.sleep(500); + await utils.dragToRight(300, 300); + await browser.sleep(500); + + const stream = await utils.waitForElement('.OV_stream.local'); + const streamProps: IRectangle = await stream.getRect(); + expect(streamProps.x).equals(300 + marginX); + expect(streamProps.y).equals(300); + }); + + it('should be the MINIMIZED video ALWAYS VISIBLE when toggling panels', async () => { + await browser.get(`${url}&prejoin=false&audioEnabled=false`); + const marginX = 5; + + await utils.checkLayoutPresent(); + await utils.subscribeToDropEvent(); + + // Minimize stream element + await utils.waitForElement('.OV_stream.local'); + await utils.hoverOn('.OV_stream.local'); + await utils.waitForElement('#minimize-btn'); + await utils.clickOn('#minimize-btn'); + + await browser.sleep(500); + await utils.dragToRight(900, 0); + await browser.sleep(500); + + let stream = await utils.waitForElement('.OV_stream.local'); + let streamProps: IRectangle = await stream.getRect(); + expect(streamProps.x).equals(900 + marginX); + expect(streamProps.y).equals(0); + + // Open chat panel + await utils.clickOn('#chat-panel-btn'); + await browser.sleep(1000); + stream = await utils.waitForElement('.OV_stream.local'); + streamProps = await stream.getRect(); + let lastX = streamProps.x; + + expect(streamProps.x).lessThan(900 + marginX); + expect(streamProps.y).equals(0); + + // Close chat panel + await utils.clickOn('#chat-panel-btn'); + await browser.sleep(1000); + + stream = await utils.waitForElement('.OV_stream.local'); + streamProps = await stream.getRect(); + + expect(streamProps.x).greaterThan(lastX + marginX); + expect(streamProps.y).equals(0); + }); + + it('should be the MINIMIZED video go to the right when panel closes', async () => { + await browser.get(`${url}&prejoin=false&audioEnabled=false`); + const waitTimeout = 1000; + const marginX = 5; + const newX = 641 - marginX; + + await utils.checkLayoutPresent(); + await utils.subscribeToDropEvent(); + + // Open chat panel + await utils.waitForElement('.OV_stream.local'); + await utils.waitForElement('#chat-panel-btn'); + await utils.clickOn('#chat-panel-btn'); + + // Minimize stream element + await browser.sleep(waitTimeout); + await utils.hoverOn('.OV_stream.local'); + await utils.waitForElement('#minimize-btn'); + await utils.clickOn('#minimize-btn'); + + await browser.sleep(waitTimeout); + await utils.dragToRight(newX, 0); + await browser.sleep(waitTimeout); + + let stream = await utils.waitForElement('.OV_stream.local'); + let streamProps: IRectangle = await stream.getRect(); + expect(streamProps.x).equals(newX + marginX); + expect(streamProps.y).equals(0); + + // Close chat panel + // There is a unstable behaviour simulating the drag and drop with selenium (the stream is not moved the first time) + // So we are going to open and close the chat panel two times to ensure the stream is moved + await utils.clickOn('#chat-panel-btn'); + await browser.sleep(waitTimeout); + await utils.clickOn('#chat-panel-btn'); + await browser.sleep(waitTimeout); + await utils.clickOn('#chat-panel-btn'); + await browser.sleep(1000); + stream = await utils.waitForElement('.OV_stream.local'); + streamProps = await stream.getRect(); + let lastX = streamProps.x; + + expect(streamProps.x).greaterThanOrEqual(newX + marginX); + expect(streamProps.y).equals(0); + + // Open chat panel + await utils.clickOn('#chat-panel-btn'); + await browser.sleep(waitTimeout); + + stream = await utils.waitForElement('.OV_stream.local'); + streamProps = await stream.getRect(); + + expect(streamProps.x).lessThan(lastX + marginX); + expect(streamProps.y).equals(0); + }); + + it('should be the MINIMIZED video ALWAYS VISIBLE when toggling from small to bigger panel', async () => { + await browser.get(`${url}&prejoin=false&audioEnabled=false`); + const marginX = 5; + + await utils.checkLayoutPresent(); + await utils.subscribeToDropEvent(); + + // Minimize stream element + await utils.waitForElement('.OV_stream.local'); + await utils.hoverOn('.OV_stream.local'); + await utils.waitForElement('#minimize-btn'); + await utils.clickOn('#minimize-btn'); + + await browser.sleep(500); + await utils.dragToRight(900, 0); + await browser.sleep(500); + + let stream = await utils.waitForElement('.OV_stream.local'); + let streamProps: IRectangle = await stream.getRect(); + expect(streamProps.x).equals(900 + marginX); + expect(streamProps.y).equals(0); + + // Open chat panel + await utils.clickOn('#chat-panel-btn'); + await browser.sleep(1000); + stream = await utils.waitForElement('.OV_stream.local'); + streamProps = await stream.getRect(); + let lastX = streamProps.x; + + expect(streamProps.x).lessThan(900 + marginX); + expect(streamProps.y).equals(0); + + // Open settings panel + await utils.togglePanel('settings'); + await browser.sleep(1000); + + stream = await utils.waitForElement('.OV_stream.local'); + streamProps = await stream.getRect(); + + expect(streamProps.x).lessThan(lastX + marginX); + expect(streamProps.y).equals(0); + lastX = streamProps.x; + + // Open chat panel + await utils.clickOn('#chat-panel-btn'); + await browser.sleep(1000); + stream = await utils.waitForElement('.OV_stream.local'); + streamProps = await stream.getRect(); + + expect(streamProps.x).greaterThan(lastX + marginX); + expect(streamProps.y).equals(0); + }); + + it('should show the audio detection elements when participant is speaking', async () => { + const roomName = 'speakingE2E'; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`; + await browser.get(`${fixedUrl}&audioEnabled=false`); + + await utils.checkLayoutPresent(); + + // Starting new browser for adding the second participant + const newTabScript = `window.open("${fixedUrl}")`; + await browser.executeScript(newTabScript); + const tabs = await browser.getAllWindowHandles(); + await browser.switchTo().window(tabs[0]); + + await utils.waitForElement('.OV_stream.remote.speaking'); + expect(await utils.getNumberOfElements('.OV_stream.remote.speaking')).to.be.equal(1); + expect(await utils.getNumberOfElements('.OV_stream.speaking')).to.be.equal(1); + }); +}); + +describe('Testing video is playing', () => { + let browser: WebDriver; + let utils: OpenViduComponentsPO; + + async function createChromeBrowser(): Promise { + return await new Builder() + .forBrowser(WebComponentConfig.browserName) + .withCapabilities(WebComponentConfig.browserCapabilities) + .setChromeOptions(WebComponentConfig.browserOptions) + .usingServer(WebComponentConfig.seleniumAddress) + .build(); + } + + beforeEach(async () => { + browser = await createChromeBrowser(); + utils = new OpenViduComponentsPO(browser); + }); + + afterEach(async () => { + await browser.quit(); + }); + + it('should play the participant video with only audio', async () => { + const roomName = 'audioOnlyE2E'; + const fixedUrl = `${url}&roomName=${roomName}`; + await browser.get(fixedUrl); + + await utils.checkPrejoinIsPresent(); + await utils.clickOn('#join-button'); + + // Starting new browser for adding the second participant + const newTabScript = `window.open("${fixedUrl}")`; + await browser.executeScript(newTabScript); + const tabs = await browser.getAllWindowHandles(); + await browser.switchTo().window(tabs[1]); + + await utils.checkPrejoinIsPresent(); + await utils.clickOn('#camera-button'); + await utils.clickOn('#join-button'); + + // Go to first tab + await browser.switchTo().window(tabs[0]); + + // Wait until NO_STREAM_PLAYING_EVENT exception timeout is reached + await browser.sleep(6000); + + const exceptionQuantity = await utils.getNumberOfElements('#NO_STREAM_PLAYING_EVENT'); + expect(exceptionQuantity).equals(0); + }); + + it('should play the participant video with only video', async () => { + const roomName = 'videoOnlyE2E'; + const fixedUrl = `${url}&roomName=${roomName}`; + await browser.get(fixedUrl); + + await utils.checkPrejoinIsPresent(); + await utils.clickOn('#join-button'); + + // Starting new browser for adding the second participant + const newTabScript = `window.open("${fixedUrl}")`; + await browser.executeScript(newTabScript); + const tabs = await browser.getAllWindowHandles(); + await browser.switchTo().window(tabs[1]); + + await utils.checkPrejoinIsPresent(); + await utils.clickOn('#microphone-button'); + await utils.clickOn('#join-button'); + + // Go to first tab + await browser.switchTo().window(tabs[0]); + + // Wait until NO_STREAM_PLAYING_EVENT exception timeout is reached + await browser.sleep(6000); + + const exceptionQuantity = await utils.getNumberOfElements('#NO_STREAM_PLAYING_EVENT'); + expect(exceptionQuantity).equals(0); + }); +}); diff --git a/openvidu-components-angular/e2e/webcomponent-e2e/toolbar.test.ts b/openvidu-components-angular/e2e/webcomponent-e2e/toolbar.test.ts new file mode 100644 index 00000000..0accf7dc --- /dev/null +++ b/openvidu-components-angular/e2e/webcomponent-e2e/toolbar.test.ts @@ -0,0 +1,63 @@ +import { expect } from 'chai'; +import { Builder, WebDriver } from 'selenium-webdriver'; +import { OPENVIDU_CALL_SERVER } from '../config'; +import { WebComponentConfig } from '../selenium.conf'; +import { OpenViduComponentsPO } from '../utils.po.test'; + +const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`; + +describe('Testing TOOLBAR features', () => { + let browser: WebDriver; + let utils: OpenViduComponentsPO; + async function createChromeBrowser(): Promise { + return await new Builder() + .forBrowser(WebComponentConfig.browserName) + .withCapabilities(WebComponentConfig.browserCapabilities) + .setChromeOptions(WebComponentConfig.browserOptions) + .usingServer(WebComponentConfig.seleniumAddress) + .build(); + } + + beforeEach(async () => { + browser = await createChromeBrowser(); + utils = new OpenViduComponentsPO(browser); + }); + + afterEach(async () => { + await browser.quit(); + }); + + it('should mute and unmute the local microphone', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkLayoutPresent(); + + const micButton = await utils.waitForElement('#mic-btn'); + await micButton.click(); + + await utils.waitForElement('#mic-btn #mic_off'); + expect(await utils.isPresent('#mic-btn #mic_off')).to.be.true; + + await micButton.click(); + + await utils.waitForElement('#mic-btn #mic'); + expect(await utils.isPresent('#mic-btn #mic')).to.be.true; + }); + + it('should mute and unmute the local camera', async () => { + await browser.get(`${url}&prejoin=false`); + + await utils.checkLayoutPresent(); + + const cameraButton = await utils.waitForElement('#camera-btn'); + await cameraButton.click(); + + await utils.waitForElement('#camera-btn #videocam_off'); + expect(await utils.isPresent('#camera-btn #videocam_off')).to.be.true; + + await cameraButton.click(); + + await utils.waitForElement('#camera-btn #videocam'); + expect(await utils.isPresent('#camera-btn #videocam')).to.be.true; + }); +}); diff --git a/openvidu-components-angular/e2e/webcomponent.pro.test.ts b/openvidu-components-angular/e2e/webcomponent.pro.test.ts index 74ff1e05..a83a7d7e 100644 --- a/openvidu-components-angular/e2e/webcomponent.pro.test.ts +++ b/openvidu-components-angular/e2e/webcomponent.pro.test.ts @@ -1,10 +1,10 @@ import { expect } from 'chai'; import { Builder, WebDriver } from 'selenium-webdriver'; -import { OPENVIDU_SECRET, OPENVIDU_SERVER_URL } from './config'; +import { OPENVIDU_CALL_SERVER } from './config'; import { WebComponentConfig } from './selenium.conf'; import { OpenViduComponentsPO } from './utils.po.test'; -const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_SERVER_URL}&OV_SECRET=${OPENVIDU_SECRET}`; +const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`; describe('Testing API Directives', () => { let browser: WebDriver; @@ -42,8 +42,8 @@ describe('Testing API Directives', () => { await browser.sleep(500); // Checking if button panel is present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; + await utils.waitForElement('#more-options-menu'); + expect(await utils.isPresent('#more-options-menu')).to.be.true; // Checking if captions button is present await utils.waitForElement('#captions-btn'); @@ -81,8 +81,8 @@ describe('Testing API Directives', () => { await browser.sleep(500); // Checking if button panel is present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; + await utils.waitForElement('#more-options-menu'); + expect(await utils.isPresent('#more-options-menu')).to.be.true; // Checking if captions button is present await utils.waitForElement('#captions-btn'); @@ -199,8 +199,8 @@ describe('Testing captions features', () => { await browser.sleep(500); // Checking if button panel is present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; + await utils.waitForElement('#more-options-menu'); + expect(await utils.isPresent('#more-options-menu')).to.be.true; // Checking if captions button is present await utils.waitForElement('#captions-btn'); @@ -224,8 +224,8 @@ describe('Testing captions features', () => { await browser.sleep(500); // Checking if button panel is present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; + await utils.waitForElement('#more-options-menu'); + expect(await utils.isPresent('#more-options-menu')).to.be.true; // Checking if captions button is present await utils.waitForElement('#captions-btn'); @@ -261,8 +261,8 @@ describe('Testing captions features', () => { await browser.sleep(500); // Checking if button panel is present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; + await utils.waitForElement('#more-options-menu'); + expect(await utils.isPresent('#more-options-menu')).to.be.true; // Checking if captions button is present await utils.waitForElement('#captions-btn'); @@ -304,8 +304,8 @@ describe('Testing captions features', () => { await browser.sleep(500); // Checking if button panel is present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; + await utils.waitForElement('#more-options-menu'); + expect(await utils.isPresent('#more-options-menu')).to.be.true; // Checking if captions button is present await utils.waitForElement('#captions-btn'); diff --git a/openvidu-components-angular/e2e/webcomponent.test.ts b/openvidu-components-angular/e2e/webcomponent.test.ts index fa44a4fe..a45048b4 100644 --- a/openvidu-components-angular/e2e/webcomponent.test.ts +++ b/openvidu-components-angular/e2e/webcomponent.test.ts @@ -1,1946 +1,11 @@ import { expect } from 'chai'; import { Builder, Key, WebDriver } from 'selenium-webdriver'; -import { OPENVIDU_SECRET, OPENVIDU_SERVER_URL } from './config'; +import { OPENVIDU_CALL_SERVER } from './config'; import { getBrowserOptionsWithoutDevices, WebComponentConfig } from './selenium.conf'; import { OpenViduComponentsPO } from './utils.po.test'; -const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_SERVER_URL}&OV_SECRET=${OPENVIDU_SECRET}`; +const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`; -describe('Testing API Directives', () => { - let browser: WebDriver; - let utils: OpenViduComponentsPO; - async function createChromeBrowser(): Promise { - return await new Builder() - .forBrowser(WebComponentConfig.browserName) - .withCapabilities(WebComponentConfig.browserCapabilities) - .setChromeOptions(WebComponentConfig.browserOptions) - .usingServer(WebComponentConfig.seleniumAddress) - .build(); - } - - beforeEach(async () => { - browser = await createChromeBrowser(); - utils = new OpenViduComponentsPO(browser); - }); - - afterEach(async () => { - // console.log('data:image/png;base64,' + await browser.takeScreenshot()); - await browser.quit(); - }); - - it('should join with ONLY ONE TOKEN', async () => { - await browser.get(`${url}&singleToken=true`); - - // Checking if prejoin page exist - await utils.checkPrejoinIsPresent(); - - const joinButton = await utils.waitForElement('#join-button'); - await joinButton.click(); - - // Checking if session container is present - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - // Checking if screenshare button is not present - expect(await utils.isPresent('#screenshare-btn')).to.be.false; - }); - - it('should set the MINIMAL UI', async () => { - await browser.get(`${url}&minimal=true`); - // Checking if prejoin page exist - await utils.checkPrejoinIsPresent(); - - // Checking if layout is present - await utils.checkLayoutPresent(); - - // Checking if stream component is present - utils.checkStreamIsPresent(); - - // Checking if audio detection is not displayed - expect(await utils.isPresent('#audio-wave-container')).to.be.false; - - const joinButton = await utils.waitForElement('#join-button'); - await joinButton.click(); - - // Checking if session container is present - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Checking if screenshare button is not present - expect(await utils.isPresent('#screenshare-btn')).to.be.false; - - // Checking if more options button is not present - expect(await utils.isPresent('#more-options-btn')).to.be.false; - - // Checking if participants panel button is not present - expect(await utils.isPresent('#participants-panel-btn')).to.be.false; - - // Checking if activities panel button is not present - expect(await utils.isPresent('#activities-panel-btn')).to.be.false; - - // Checking if logo is not displayed - expect(await utils.isPresent('#branding-logo')).to.be.false; - - // Checking if session name is not displayed - expect(await utils.isPresent('#session-name')).to.be.false; - - // Checking if nickname is not displayed - expect(await utils.getNumberOfElements('#nickname-container')).equals(0); - - // Checking if audio detection is not displayed - expect(await utils.isPresent('#audio-wave-container')).to.be.false; - - // Checking if settings button is not displayed - expect(await utils.isPresent('#settings-container')).to.be.false; - }); - - it('should change the UI LANG ', async () => { - await browser.get(`${url}&lang=es`); - - await utils.checkPrejoinIsPresent(); - - let element = await utils.waitForElement('.lang-button'); - expect(await element.getText()).equal('Españolexpand_more'); - - element = await utils.waitForElement('#join-button'); - expect(await element.getText()).equal('Unirme ahora'); - }); - - it('should override the LANG OPTIONS', async () => { - await browser.get(`${url}&prejoin=true&langOptions=true`); - - - await utils.checkPrejoinIsPresent(); - await utils.waitForElement('.lang-button'); - await utils.clickOn('.lang-button'); - await browser.sleep(500); - expect(await utils.getNumberOfElements('.lang-menu-opt')).equals(2); - - await utils.clickOn('.lang-menu-opt'); - await browser.sleep(500); - - await utils.clickOn('#join-button'); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Open more options menu - await utils.clickOn('#more-options-btn'); - - await browser.sleep(500); - - // Checking if button panel is present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; - - // Checking if captions button is present - await utils.waitForElement('#toolbar-settings-btn'); - expect(await utils.isPresent('#toolbar-settings-btn')).to.be.true; - await utils.clickOn('#toolbar-settings-btn'); - - await browser.sleep(500); - - await utils.waitForElement('#settings-container'); - await utils.waitForElement('.lang-button'); - await utils.clickOn('.lang-button'); - - await browser.sleep(500); - - expect(await utils.getNumberOfElements('.lang-menu-opt')).equals(2); - - }); - - it('should show the PREJOIN page', async () => { - await browser.get(`${url}&prejoin=true`); - - await utils.checkPrejoinIsPresent(); - }); - - it('should not show the PREJOIN page', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - }); - - it('should run the app with VIDEO MUTED in prejoin page', async () => { - try { - let idVideoEnabled; - const script = 'return document.getElementsByTagName("video")[0].srcObject.getVideoTracks()[0].enabled;'; - - await browser.get(`${url}&prejoin=true&videoMuted=true`); - - await utils.checkPrejoinIsPresent(); - - // Checking if video is displayed - await utils.checkVideoElementIsPresent(); - - // Checking if virtual background button is disabled - const button = await utils.waitForElement('#background-effects-btn'); - expect(await button.isEnabled()).to.be.false; - - // Checking if video track is disabled/muted - idVideoEnabled = await browser.executeScript(script); - expect(idVideoEnabled).to.be.false; - - await utils.waitForElement('#videocam_off'); - await utils.clickOn('#join-button'); - - // Checking if video is muted after join the room - await utils.checkSessionIsPresent(); - - idVideoEnabled = await browser.executeScript(script); - expect(idVideoEnabled).to.be.false; - - await utils.waitForElement('#videocam_off'); - expect(await utils.isPresent('#videocam_off')).to.be.true; - } catch (error) { - console.log(error); - console.log(await browser.takeScreenshot()); - } - }); - - it('should run the app with VIDEO MUTED and WITHOUT PREJOIN page', async () => { - let isVideoEnabled; - const videoEnableScript = 'return document.getElementsByTagName("video")[0].srcObject.getVideoTracks()[0].enabled;'; - - await browser.get(`${url}&prejoin=false&videoMuted=true`); - - await browser.sleep(2000); - - await utils.checkSessionIsPresent(); - - await utils.checkLayoutPresent(); - - // Checking if video is displayed - await utils.checkVideoElementIsPresent(); - - // Checking if video track is disabled/muted - isVideoEnabled = await browser.executeScript(videoEnableScript); - expect(isVideoEnabled).to.be.false; - - await utils.waitForElement('#videocam_off'); - expect(await utils.isPresent('#videocam_off')).to.be.true; - }); - - it('should run the app with AUDIO MUTED in prejoin page', async () => { - let isAudioEnabled; - const script = 'return document.getElementsByTagName("video")[0].srcObject.getAudioTracks()[0].enabled;'; - - await browser.get(`${url}&audioMuted=true`); - - await utils.checkPrejoinIsPresent(); - - // Checking if video is displayed - await utils.checkVideoElementIsPresent(); - - // Checking if audio track is disabled/muted - isAudioEnabled = await browser.executeScript(script); - expect(isAudioEnabled).to.be.false; - - await utils.waitForElement('#mic_off'); - expect(await utils.isPresent('#mic_off')).to.be.true; - - await utils.clickOn('#join-button'); - - // Checking if audio is muted after join the room - await utils.checkSessionIsPresent(); - isAudioEnabled = await browser.executeScript(script); - expect(isAudioEnabled).to.be.false; - - await utils.waitForElement('#mic_off'); - expect(await utils.isPresent('#mic_off')).to.be.true; - }); - - it('should run the app with AUDIO MUTED and WITHOUT PREJOIN page', async () => { - let isAudioEnabled; - const audioEnableScript = 'return document.getElementsByTagName("video")[0].srcObject.getAudioTracks()[0].enabled;'; - - await browser.get(`${url}&prejoin=false&audioMuted=true`); - - await utils.checkSessionIsPresent(); - - // Checking if video is displayed - await utils.checkVideoElementIsPresent(); - - // Checking if audio track is disabled/muted - isAudioEnabled = await browser.executeScript(audioEnableScript); - expect(isAudioEnabled).to.be.false; - - await utils.waitForElement('#mic_off'); - expect(await utils.isPresent('#mic_off')).to.be.true; - }); - - it('should HIDE the SCREENSHARE button', async () => { - await browser.get(`${url}&prejoin=false&screenshareBtn=false`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Checking if screenshare button is not present - expect(await utils.isPresent('#screenshare-btn')).to.be.false; - }); - - it('should HIDE the FULLSCREEN button', async () => { - await browser.get(`${url}&prejoin=false&fullscreenBtn=false`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Open more options menu - await utils.clickOn('#more-options-btn'); - - await browser.sleep(500); - - // Checking if fullscreen button is not present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; - expect(await utils.getNumberOfElements('#fullscreen-btn')).equals(0); - - }); - - it('should HIDE the CAPTIONS button', async () => { - await browser.get(`${url}&prejoin=false&toolbarCaptionsBtn=false`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Open more options menu - await utils.clickOn('#more-options-btn'); - - await browser.sleep(500); - - // Checking if button panel is present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; - - // Checking if captions button is not present - expect(await utils.isPresent('#captions-btn')).to.be.false; - - await utils.clickOn('#toolbar-settings-btn'); - - await browser.sleep(500); - - await utils.waitForElement('.settings-container'); - expect(await utils.isPresent('.settings-container')).to.be.true; - - expect(await utils.isPresent('#captions-opt')).to.be.false; - }); - - it('should HIDE the TOOLBAR RECORDING button', async () => { - await browser.get(`${url}&prejoin=false&toolbarRecordingButton=false`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Open more options menu - await utils.clickOn('#more-options-btn'); - - await browser.sleep(500); - - // Checking if button panel is present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; - - // Checking if recording button is not present - expect(await utils.isPresent('#recording-btn')).to.be.false; - }); - - it('should HIDE the TOOLBAR BROADCASTING button', async () => { - await browser.get(`${url}&prejoin=false&toolbarBroadcastingButton=false`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Open more options menu - await utils.clickOn('#more-options-btn'); - - await browser.sleep(500); - - // Checking if button panel is present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; - - // Checking if broadcasting button is not present - expect(await utils.isPresent('#broadcasting-btn')).to.be.false; - }); - - it('should HIDE the TOOLBAR SETTINGS button', async () => { - await browser.get(`${url}&prejoin=false&toolbarSettingsBtn=false`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Open more options menu - await utils.clickOn('#more-options-btn'); - - await browser.sleep(500); - - // Checking if fullscreen button is not present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; - - expect(await utils.isPresent('#toolbar-settings-btn')).to.be.false; - }); - - it('should HIDE the LEAVE button', async () => { - await browser.get(`${url}&prejoin=false&leaveBtn=false`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Checking if leave button is not present - expect(await utils.getNumberOfElements('#leave-btn')).equals(0); - }); - - it('should HIDE the ACTIVITIES PANEL button', async () => { - await browser.get(`${url}&prejoin=false&activitiesPanelBtn=false`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Checking if activities panel button is not present - expect(await utils.isPresent('#activities-panel-btn')).to.be.false; - }); - - it('should HIDE the CHAT PANEL button', async () => { - await browser.get(`${url}&prejoin=false&chatPanelBtn=false`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Checking if chat panel button is not present - expect(await utils.isPresent('#chat-panel-btn')).to.be.false; - }); - - it('should HIDE the PARTICIPANTS PANEL button', async () => { - await browser.get(`${url}&prejoin=false&participantsPanelBtn=false`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Checking if participants panel button is not present - expect(await utils.isPresent('#participants-panel-btn')).to.be.false; - }); - - it('should HIDE the LOGO', async () => { - await browser.get(`${url}&prejoin=false&displayLogo=false`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Checking if toolbar is present - await utils.waitForElement('#info-container'); - expect(await utils.isPresent('#info-container')).to.be.true; - - // Checking if logo is not displayed - expect(await utils.isPresent('#branding-logo')).to.be.false; - }); - - it('should HIDE the SESSION NAME', async () => { - await browser.get(`${url}&prejoin=false&displaySessionName=false`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Checking if toolbar is present - await utils.waitForElement('#info-container'); - expect(await utils.isPresent('#info-container')).to.be.true; - - // Checking if session name is not displayed - expect(await utils.isPresent('#session-name')).to.be.false; - }); - - it('should HIDE the PARTICIPANT NAME', async () => { - await browser.get(`${url}&prejoin=false&displayParticipantName=false`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Checking if stream component is present - await utils.checkStreamIsPresent(); - - // Checking if nickname is not present - expect(await utils.isPresent('#nickname-container')).to.be.false; - }); - - it('should HIDE the AUDIO DETECTION element', async () => { - await browser.get(`${url}&prejoin=false&displayAudioDetection=false`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Checking if stream component is present - await utils.checkStreamIsPresent(); - - // Checking if audio detection is not present - expect(await utils.isPresent('#audio-wave-container')).to.be.false; - }); - - it('should HIDE the STREAM SETTINGS button', async () => { - await browser.get(`${url}&prejoin=false&settingsBtn=false`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Checking if stream component is present - await utils.checkStreamIsPresent(); - - // Checking if settings button is not present - expect(await utils.isPresent('#settings-container')).to.be.false; - }); - - it('should HIDE the MUTE button in participants panel', async () => { - const sessionName = 'e2etest'; - const fixedUrl = `${url}&prejoin=false&participantMuteBtn=false&sessionName=${sessionName}`; - await browser.get(fixedUrl); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - const participantsButton = await utils.waitForElement('#participants-panel-btn'); - await participantsButton.click(); - - // Checking if participatns panel is displayed - await utils.waitForElement('#participants-container'); - expect(await utils.isPresent('#participants-container')).to.be.true; - - // Checking remote participants item - expect(await utils.isPresent('#remote-participant-item')).to.be.false; - - // Starting new browser for adding a new participant - const newTabScript = `window.open("${fixedUrl}")`; - await browser.executeScript(newTabScript); - - // Go to first tab - const tabs = await browser.getAllWindowHandles(); - browser.switchTo().window(tabs[0]); - - // Checking if mute button is not displayed in participant item - await utils.waitForElement('#remote-participant-item'); - expect(await utils.isPresent('#remote-participant-item')).to.be.true; - - expect(await utils.isPresent('#mute-btn')).to.be.false; - }); - - it('should HIDE the RECORDING ACTIVITY in activities panel', async () => { - let element; - const fixedUrl = `${url}&prejoin=false&activitiesPanelRecordingActivity=false`; - await browser.get(fixedUrl); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - element = await utils.waitForElement('#activities-panel-btn'); - await element.click(); - - // Checking if participatns panel is displayed - await utils.waitForElement('#default-activities-panel'); - expect(await utils.isPresent('#default-activities-panel')).to.be.true; - - // await browser.sleep(1000); - - // Checking if recording activity exists - await utils.waitForElement('.activities-body-container'); - expect(await utils.isPresent('ov-recording-activity')).to.be.false; - }); - - it('should SHOW a RECORDING ERROR in activities panel', async () => { - let element; - const fixedUrl = `${url}&prejoin=false&recordingError=TEST_ERROR`; - await browser.get(fixedUrl); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - element = await utils.waitForElement('#activities-panel-btn'); - await element.click(); - - // Checking if participatns panel is displayed - await utils.waitForElement('#default-activities-panel'); - expect(await utils.isPresent('#default-activities-panel')).to.be.true; - - // Checking if recording activity exists - await utils.waitForElement('#activities-container'); - await utils.waitForElement('.activities-body-container'); - - await utils.waitForElement('ov-recording-activity'); - expect(await utils.isPresent('ov-recording-activity')).to.be.true; - - await utils.waitForElement('.failed'); - expect(await utils.isPresent('.failed')).to.be.true; - - // Open recording - await browser.sleep(1000); - element = await utils.waitForElement('ov-recording-activity'); - await element.click(); - - element = await utils.waitForElement('.recording-error'); - expect(await element.getAttribute('innerText')).equal('"TEST_ERROR"'); - expect(await utils.isPresent('.recording-error')).to.be.true; - }); - - it('should SHOW a BROADCASTING ERROR in activities panel', async () => { - let element; - const fixedUrl = `${url}&prejoin=false&broadcastingError=TEST_ERROR`; - await browser.get(fixedUrl); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - element = await utils.waitForElement('#activities-panel-btn'); - await element.click(); - - // Checking if participatns panel is displayed - await utils.waitForElement('#default-activities-panel'); - expect(await utils.isPresent('#default-activities-panel')).to.be.true; - - // Checking if broadcasting activity exists - await utils.waitForElement('#activities-container'); - await utils.waitForElement('.activities-body-container'); - - await utils.waitForElement('ov-broadcasting-activity'); - expect(await utils.isPresent('ov-broadcasting-activity')).to.be.true; - - const status = await utils.waitForElement('#broadcasting-status'); - expect(await status.getAttribute('innerText')).equals('FAILED'); - - // Open broadcasting - await browser.sleep(1000); - await utils.clickOn('ov-broadcasting-activity'); - - element = await utils.waitForElement('#broadcasting-error'); - expect(await element.getAttribute('innerText')).equal('TEST_ERROR'); - }); - - it('should HIDE the BROADCASTING ACTIVITY in activities panel', async () => { - await browser.get(`${url}&prejoin=false&activitiesPanelBroadcastingActivity=false`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - await utils.waitForElement('#activities-panel-btn'); - await utils.clickOn('#activities-panel-btn'); - - // Checking if participatns panel is displayed - await utils.waitForElement('#default-activities-panel'); - expect(await utils.isPresent('#default-activities-panel')).to.be.true; - - // await browser.sleep(1000); - - // Checking if recording activity exists - await utils.waitForElement('.activities-body-container'); - expect(await utils.isPresent('ov-broadcasting-activity')).to.be.false; - }); -}); - -describe('Testing videoconference EVENTS', () => { - let browser: WebDriver; - let utils: OpenViduComponentsPO; - async function createChromeBrowser(): Promise { - return await new Builder() - .forBrowser(WebComponentConfig.browserName) - .withCapabilities(WebComponentConfig.browserCapabilities) - .setChromeOptions(WebComponentConfig.browserOptions) - .usingServer(WebComponentConfig.seleniumAddress) - .build(); - } - - beforeEach(async () => { - browser = await createChromeBrowser(); - utils = new OpenViduComponentsPO(browser); - }); - - afterEach(async () => { - await browser.quit(); - }); - - it('should receive the onJoinButtonClicked event', async () => { - await browser.get(`${url}`); - - await utils.waitForElement('#prejoin-container'); - expect(await utils.isPresent('#prejoin-container')).to.be.true; - - // Clicking to join button - await utils.waitForElement('#join-button'); - await utils.clickOn('#join-button'); - - // Checking if onJoinButtonClicked has been received - await utils.waitForElement('#onJoinButtonClicked'); - expect(await utils.isPresent('#onJoinButtonClicked')).to.be.true; - }); - - it('should receive the onToolbarLeaveButtonClicked event', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - // Clicking to leave button - const leaveButton = await utils.waitForElement('#leave-btn'); - expect(await utils.isPresent('#leave-btn')).to.be.true; - await leaveButton.click(); - - // Checking if onToolbarLeaveButtonClicked has been received - await utils.waitForElement('#onToolbarLeaveButtonClicked'); - expect(await utils.isPresent('#onToolbarLeaveButtonClicked')).to.be.true; - }); - - it('should receive the onToolbarCameraButtonClicked event', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - // Clicking to leave button - const cameraButton = await utils.waitForElement('#camera-btn'); - expect(await utils.isPresent('#camera-btn')).to.be.true; - await cameraButton.click(); - - // Checking if onToolbarCameraButtonClicked has been received - await utils.waitForElement('#onToolbarCameraButtonClicked'); - expect(await utils.isPresent('#onToolbarCameraButtonClicked')).to.be.true; - }); - - it('should receive the onToolbarMicrophoneButtonClicked event', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - // Clicking to mic button - const micButton = await utils.waitForElement('#mic-btn'); - expect(await utils.isPresent('#mic-btn')).to.be.true; - await micButton.click(); - - // Checking if onToolbarMicrophoneButtonClicked has been received - await utils.waitForElement('#onToolbarMicrophoneButtonClicked'); - expect(await utils.isPresent('#onToolbarMicrophoneButtonClicked')).to.be.true; - }); - - it('should receive the onToolbarScreenshareButtonClicked event', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - // Clicking to leave button - const screenshareButton = await utils.waitForElement('#screenshare-btn'); - expect(await utils.isPresent('#screenshare-btn')).to.be.true; - await screenshareButton.click(); - - // Checking if onToolbarScreenshareButtonClicked has been received - await utils.waitForElement('#onToolbarScreenshareButtonClicked'); - expect(await utils.isPresent('#onToolbarScreenshareButtonClicked')).to.be.true; - }); - - it('should receive the onToolbarFullscreenButtonClicked event', async () => { - let element; - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - // Open more options menu - element = await utils.waitForElement('#more-options-btn'); - expect(await utils.isPresent('#more-options-btn')).to.be.true; - await element.click(); - - // Clicking to fullscreen button - await utils.waitForElement('.mat-mdc-menu-content'); - - const fullscreenButton = await utils.waitForElement('#fullscreen-btn'); - expect(await utils.isPresent('#fullscreen-btn')).to.be.true; - await fullscreenButton.click(); - - // Checking if onToolbarFullscreenButtonClicked has been received - await utils.waitForElement('#onToolbarFullscreenButtonClicked'); - expect(await utils.isPresent('#onToolbarFullscreenButtonClicked')).to.be.true; - }); - - it('should receive the onToolbarChatPanelButtonClicked event', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - // Clicking to chat button - const chatButton = await utils.waitForElement('#chat-panel-btn'); - await chatButton.click(); - - // Checking if onToolbarChatPanelButtonClicked has been received - await utils.waitForElement('#onToolbarChatPanelButtonClicked'); - expect(await utils.isPresent('#onToolbarChatPanelButtonClicked')).to.be.true; - }); - - it('should receive the onToolbarParticipantsPanelButtonClicked event', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - // Clicking to participants button - const participantsButton = await utils.waitForElement('#participants-panel-btn'); - await participantsButton.click(); - - // Checking if onToolbarParticipantsPanelButtonClicked has been received - await utils.waitForElement('#onToolbarParticipantsPanelButtonClicked'); - expect(await utils.isPresent('#onToolbarParticipantsPanelButtonClicked')).to.be.true; - }); - - it('should receive the onToolbarActivitiesPanelButtonClicked event', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - // Clicking to activities button - const activitiesButton = await utils.waitForElement('#activities-panel-btn'); - await activitiesButton.click(); - - // Checking if onToolbarActivitiesPanelButtonClicked has been received - await utils.waitForElement('#onToolbarActivitiesPanelButtonClicked'); - expect(await utils.isPresent('#onToolbarActivitiesPanelButtonClicked')).to.be.true; - }); - - it('should receive the onToolbarStartRecordingClicked event', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - await utils.checkToolbarIsPresent(); - - // Open more options menu - await utils.waitForElement('#more-options-btn'); - expect(await utils.isPresent('#more-options-btn')).to.be.true; - await utils.clickOn('#more-options-btn'); - - await browser.sleep(500); - - // Clicking to recording button - await utils.waitForElement('.mat-mdc-menu-content'); - - await utils.waitForElement('#recording-btn'); - expect(await utils.isPresent('#recording-btn')).to.be.true; - await utils.clickOn('#recording-btn'); - - // Checking if onToolbarStartRecordingClicked has been received - await utils.waitForElement('#onToolbarStartRecordingClicked'); - expect(await utils.isPresent('#onToolbarStartRecordingClicked')).to.be.true; - }); - - // TODO: it needs an OpenVidu PRO - // it('should receive the onToolbarStopBroadcastingClicked event', async () => { - // await browser.get(`${url}&prejoin=false`); - - // await utils.checkSessionIsPresent(); - // await utils.checkToolbarIsPresent(); - - // // Open more options menu - // await utils.waitForElement('#more-options-btn'); - // expect(await utils.isPresent('#more-options-btn')).to.be.true; - // await utils.clickOn('#more-options-btn'); - - // await browser.sleep(500); - - // await utils.waitForElement('.mat-mdc-menu-content'); - - // await utils.waitForElement('#broadcasting-btn'); - // await utils.clickOn('#broadcasting-btn'); - - // await browser.sleep(500); - - // await utils.waitForElement('.sidenav-menu'); - // await utils.waitForElement('#activities-container'); - - // await utils.waitForElement('#broadcasting-url-input'); - // const input = await utils.waitForElement('#broadcast-url-input'); - // await input.sendKeys('BroadcastUrl'); - // await utils.clickOn('#broadcasting-btn'); - - // // Open more options menu - // await utils.waitForElement('#more-options-btn'); - // expect(await utils.isPresent('#more-options-btn')).to.be.true; - // await utils.clickOn('#more-options-btn'); - - // await browser.sleep(500); - - // await utils.waitForElement('.mat-mdc-menu-content'); - - // await utils.waitForElement('#broadcasting-btn'); - // await utils.clickOn('#broadcasting-btn'); - - // // Checking if onToolbarStopBroadcastingClicked has been received - // await utils.waitForElement('#onToolbarStopBroadcastingClicked'); - // expect(await utils.isPresent('#onToolbarStopBroadcastingClicked')).to.be.true; - // }); - - it('should receive the onActivitiesPanelStartRecordingClicked event', async () => { - let element; - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - // Open activities panel - element = await utils.waitForElement('#activities-panel-btn'); - expect(await utils.isPresent('#activities-panel-btn')).to.be.true; - await element.click(); - - await browser.sleep(1000); - - // Open recording - element = await utils.waitForElement('ov-recording-activity'); - await element.click(); - - await browser.sleep(1000); - - // Clicking to recording button - element = await utils.waitForElement('#start-recording-btn'); - expect(await element.isEnabled()).to.be.true; - - await element.click(); - - // Checking if onActivitiesPanelStartRecordingClicked has been received - await utils.waitForElement('#onActivitiesPanelStartRecordingClicked'); - expect(await utils.isPresent('#onActivitiesPanelStartRecordingClicked')).to.be.true; - }); - - it('should receive the PLAY and DELETE recording events', async () => { - let element; - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - // Clicking to activities button - const activitiesButton = await utils.waitForElement('#activities-panel-btn'); - expect(await utils.isPresent('#activities-panel-btn')).to.be.true; - await activitiesButton.click(); - - await browser.sleep(1500); - // Open recording - element = await utils.waitForElement('ov-recording-activity'); - await element.click(); - - await browser.sleep(1500); - - // Delete event - element = await utils.waitForElement('#delete-recording-btn'); - expect(await utils.isPresent('#delete-recording-btn')).to.be.true; - await element.click(); - - element = await utils.waitForElement('#delete-recording-confirm-btn'); - expect(await utils.isPresent('#delete-recording-confirm-btn')).to.be.true; - await element.click(); - - await utils.waitForElement('#onActivitiesPanelDeleteRecordingClicked'); - expect(await utils.isPresent('#onActivitiesPanelDeleteRecordingClicked')).to.be.true; - }); - - it('should receive the onActivitiesPanelStartBroadcasting event', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - await utils.checkToolbarIsPresent(); - - // Get activities button and click into it - await utils.waitForElement('#activities-panel-btn'); - await utils.clickOn('#activities-panel-btn'); - await browser.sleep(500); - - await utils.waitForElement('.sidenav-menu'); - await utils.waitForElement('#activities-container'); - expect(await utils.isPresent('#activities-container')).to.be.true; - - await utils.waitForElement('#broadcasting-activity'); - await utils.clickOn('#broadcasting-activity'); - - await browser.sleep(1000); - - const button = await utils.waitForElement('#broadcasting-btn'); - expect(await button.isEnabled()).to.be.false; - - const input = await utils.waitForElement('#broadcast-url-input'); - await input.sendKeys('BroadcastUrl'); - - await utils.clickOn('#broadcasting-btn'); - - // Checking if onActivitiesPanelStartBroadcastingClicked has been received - await utils.waitForElement('#onActivitiesPanelStartBroadcastingClicked'); - expect(await utils.isPresent('#onActivitiesPanelStartBroadcastingClicked')).to.be.true; - - // TODO: it needs an OpenVidu PRO (onActivitiesPanelStopBroadcastingClicked event) - - // expect(await utils.isPresent('#broadcasting-tag')).to.be.true; - - // await utils.clickOn('#stop-broadcasting-btn'); - - // // Checking if onActivitiesPanelStopBroadcastingClicked has been received - // await utils.waitForElement('#onActivitiesPanelStopBroadcastingClicked'); - // expect(await utils.isPresent('#onActivitiesPanelStopBroadcastingClicked')).to.be.true; - // expect(await utils.isPresent('#broadcasting-tag')).to.be.false; - }); - - it('should receive the onSessionCreated event', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - await utils.waitForElement('#onSessionCreated'); - expect(await utils.isPresent('#onSessionCreated')).to.be.true; - - expect(await utils.isPresent('#onJoinButtonClicked')).to.be.false; - }); - - // * PUBLISHER EVENTS - - it('should receive onParticipantCreated event from LOCAL participant', async () => { - const participantName = 'TEST_USER'; - await browser.get(`${url}&participantName=${participantName}`); - await utils.waitForElement(`#${participantName}-onParticipantCreated`); - expect(await utils.isPresent(`#${participantName}-onParticipantCreated`)).to.be.true; - }); - - // * SESSION EVENTS - - it('should receive connectionCreated event from LOCAL participant', async () => { - const participantName = 'TEST_USER'; - await browser.get(`${url}&prejoin=false&participantName=${participantName}`); - - await utils.waitForElement(`#${participantName}-connectionCreated`); - expect(await utils.isPresent(`#${participantName}-connectionCreated`)).to.be.true; - }); - - it('should receive sessionDisconnected event from LOCAL participant', async () => { - const participantName = 'TEST_USER'; - let element; - await browser.get(`${url}&prejoin=false&participantName=${participantName}`); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - // Checking if leave button is not present - element = await utils.waitForElement('#leave-btn'); - await element.click(); - - await utils.waitForElement(`#${participantName}-sessionDisconnected`); - expect(await utils.isPresent(`#${participantName}-sessionDisconnected`)).to.be.true; - }); -}); - -describe('Testing replace track with emulated devices', () => { - let browser: WebDriver; - let utils: OpenViduComponentsPO; - async function createChromeBrowser(): Promise { - return await new Builder() - .forBrowser(WebComponentConfig.browserName) - .withCapabilities(WebComponentConfig.browserCapabilities) - .setChromeOptions(WebComponentConfig.browserOptions) - .usingServer(WebComponentConfig.seleniumAddress) - .build(); - } - - beforeEach(async () => { - browser = await createChromeBrowser(); - utils = new OpenViduComponentsPO(browser); - }); - - afterEach(async () => { - // console.log('data:image/png;base64,' + await browser.takeScreenshot()); - await browser.quit(); - }); - - it('should replace the video track in prejoin page', async () => { - const script = 'return document.getElementsByTagName("video")[0].srcObject.getVideoTracks()[0].label;'; - - await browser.get(`${url}&fakeDevices=true`); - - let videoDevices = await utils.waitForElement('#video-devices-form'); - - await videoDevices.click(); - - let element = await utils.waitForElement('#option-custom_fake_video_1'); - - await element.click(); - - let videoLabel; - - await browser.sleep(1000); - videoLabel = await browser.executeScript(script); - expect(videoLabel).to.be.equal('custom_fake_video_1'); - - await videoDevices.click(); - - element = await utils.waitForElement('#option-fake_device_0'); - await element.click(); - - await browser.sleep(1000); - videoLabel = await browser.executeScript(script); - expect(videoLabel).to.be.equal('fake_device_0'); - }); - - it('should replace the video track in videoconference page', async () => { - const script = 'return document.getElementsByTagName("video")[0].srcObject.getVideoTracks()[0].label;'; - - await browser.get(`${url}&prejoin=false&fakeDevices=true`); - - await utils.checkSessionIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Open more options menu - await utils.clickOn('#more-options-btn'); - - // Checking if button panel is present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; - - await utils.clickOn('#toolbar-settings-btn'); - - await utils.waitForElement('.settings-container'); - expect(await utils.isPresent('.settings-container')).to.be.true; - - await utils.clickOn('#video-opt'); - expect(await utils.isPresent('ov-video-devices-select')).to.be.true; - - let videoDevices = await utils.waitForElement('#video-devices-form'); - - await videoDevices.click(); - - let element = await utils.waitForElement('#option-custom_fake_video_1'); - - await element.click(); - - let videoLabel; - await browser.sleep(1000); - videoLabel = await browser.executeScript(script); - expect(videoLabel).to.be.equal('custom_fake_video_1'); - - await videoDevices.click(); - - element = await utils.waitForElement('#option-fake_device_0'); - await element.click(); - - await browser.sleep(1000); - videoLabel = await browser.executeScript(script); - expect(videoLabel).to.be.equal('fake_device_0'); - }); - - it('should replace the screen track', async () => { - const script = 'return document.getElementsByClassName("OT_video-element screen-type")[0].srcObject.getVideoTracks()[0].label;'; - - await browser.get(`${url}&prejoin=false&fakeDevices=true`); - - await utils.checkLayoutPresent(); - await utils.checkToolbarIsPresent(); - - await utils.clickOn('#screenshare-btn'); - - await browser.sleep(500); - - let screenLabel = await browser.executeScript(script); - expect(screenLabel).not.equal('custom_fake_screen'); - - await utils.clickOn('#video-settings-btn-SCREEN'); - await browser.sleep(500); - - await utils.waitForElement('.video-settings-menu'); - const replaceBtn = await utils.waitForElement('#replace-screen-button'); - await replaceBtn.sendKeys(Key.ENTER); - - await browser.sleep(1000); - screenLabel = await browser.executeScript(script); - expect(screenLabel).to.be.equal('custom_fake_screen'); - }); -}); - -describe('Testing stream video menu features', () => { - let browser: WebDriver; - let utils: OpenViduComponentsPO; - async function createChromeBrowser(): Promise { - return await new Builder() - .forBrowser(WebComponentConfig.browserName) - .withCapabilities(WebComponentConfig.browserCapabilities) - .setChromeOptions(WebComponentConfig.browserOptions) - .usingServer(WebComponentConfig.seleniumAddress) - .build(); - } - - beforeEach(async () => { - browser = await createChromeBrowser(); - utils = new OpenViduComponentsPO(browser); - }); - - afterEach(async () => { - await browser.quit(); - }); - - it('should not show the Mute sound button for local participant', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkLayoutPresent(); - - await utils.waitForElement('#video-settings-btn-CAMERA'); - await utils.clickOn('#video-settings-btn-CAMERA'); - - await browser.sleep(500); - - // Checking if mute sound button is not present - expect(await utils.isPresent('#sound-btn')).to.be.false; - }); -}); - -describe('Testing screenshare features', () => { - let browser: WebDriver; - let utils: OpenViduComponentsPO; - async function createChromeBrowser(): Promise { - return await new Builder() - .forBrowser(WebComponentConfig.browserName) - .withCapabilities(WebComponentConfig.browserCapabilities) - .setChromeOptions(WebComponentConfig.browserOptions) - .usingServer(WebComponentConfig.seleniumAddress) - .build(); - } - - beforeEach(async () => { - browser = await createChromeBrowser(); - utils = new OpenViduComponentsPO(browser); - }); - - afterEach(async () => { - await browser.quit(); - }); - - - - it('should toggle screensharing twice', async () => { - await browser.get(`${url}&prejoin=false`); - await utils.checkLayoutPresent(); - - // Clicking to screensharing button - const screenshareButton = await utils.waitForElement('#screenshare-btn'); - expect(await screenshareButton.isDisplayed()).to.be.true; - await screenshareButton.click(); - - await utils.waitForElement('.OV_big'); - expect(await utils.getNumberOfElements('video')).equals(2); - - - // Clicking to screensharing button - await screenshareButton.click(); - expect(await utils.getNumberOfElements('video')).equals(1); - - - // toggle screenshare again - await screenshareButton.click(); - - await utils.waitForElement('.OV_big'); - expect(await utils.getNumberOfElements('video')).equals(2); - - await screenshareButton.click(); - - expect(await utils.getNumberOfElements('video')).equals(1); - - }); - - it('should show the screen although toggle screensharing with video muted', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkLayoutPresent(); - - const camButton = await utils.waitForElement('#camera-btn'); - await camButton.click(); - - // Clicking to screensharing button - const screenshareButton = await utils.waitForElement('#screenshare-btn'); - expect(await screenshareButton.isDisplayed()).to.be.true; - await screenshareButton.click(); - - await browser.sleep(1000); - await utils.waitForElement('.OV_big'); - - expect(await utils.getNumberOfElements('video')).equals(2); - - - await screenshareButton.click(); - await browser.sleep(1000); - - expect(await utils.getNumberOfElements('video')).equals(1); - - }); - - it('should screensharing with audio enabled', async () => { - let isAudioEnabled; - const getAudioScript = (className: string) => { - return `return document.getElementsByClassName('${className}')[0].srcObject.getAudioTracks()[0].enabled;`; - }; - await browser.get(`${url}&prejoin=false`); - - await utils.checkLayoutPresent(); - - const micButton = await utils.waitForElement('#mic-btn'); - await micButton.click(); - - // Clicking to screensharing button - const screenshareButton = await utils.waitForElement('#screenshare-btn'); - expect(await utils.isPresent('#screenshare-btn')).to.be.true; - await screenshareButton.click(); - - await utils.waitForElement('.screen-type'); - expect(await utils.getNumberOfElements('video')).equals(2); - - - isAudioEnabled = await browser.executeScript(getAudioScript('screen-type')); - expect(isAudioEnabled).to.be.true; - - await utils.waitForElement('#statusMic'); - expect(await utils.getNumberOfElements('#statusMic')).equals(1); - - // Clicking to screensharing button - await screenshareButton.click(); - expect(await utils.getNumberOfElements('video')).equals(1); - - }); - - it('should show CAMERA stream always visible when muting video with screensharing', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkLayoutPresent(); - - // Clicking to screensharing button - const screenshareButton = await utils.waitForElement('#screenshare-btn'); - expect(await screenshareButton.isDisplayed()).to.be.true; - await screenshareButton.click(); - - await utils.waitForElement('.OV_big'); - expect(await utils.getNumberOfElements('video')).equals(2); - - const muteVideoButton = await utils.waitForElement('#camera-btn'); - await muteVideoButton.click(); - - expect(await utils.getNumberOfElements('video')).equals(2); - }); - - it('should screen audio be independent of camera audio', async () => { - let isAudioEnabled; - const audioEnableScript = 'return document.getElementsByTagName("video")[0].srcObject.getAudioTracks()[0].enabled;'; - - await browser.get(`${url}&prejoin=false`); - - await utils.checkLayoutPresent(); - - // Clicking to screensharing button - const screenshareButton = await utils.waitForElement('#screenshare-btn'); - expect(await utils.isPresent('#screenshare-btn')).to.be.true; - await screenshareButton.click(); - - await utils.waitForElement('.OV_big'); - expect(await utils.getNumberOfElements('video')).equals(2); - expect(await utils.getNumberOfElements('#statusMic')).equals(0); - - // Muting camera video - const muteVideoButton = await utils.waitForElement('#camera-btn'); - await muteVideoButton.click(); - - expect(await utils.getNumberOfElements('video')).equals(2); - - await browser.sleep(500); - expect(await utils.isPresent('#statusMic')).to.be.false; - - // Checking if audio is muted after join the room - isAudioEnabled = await browser.executeScript(audioEnableScript); - expect(isAudioEnabled).to.be.true; - - // Unmuting camera - await muteVideoButton.click(); - await browser.sleep(1000); - - await utils.waitForElement('.camera-type'); - expect(await utils.getNumberOfElements('video')).equals(2); - expect(await utils.getNumberOfElements('#statusMic')).equals(0); - }); - -}); - -describe('Testing panels', () => { - let browser: WebDriver; - let utils: OpenViduComponentsPO; - - async function createChromeBrowser(): Promise { - return await new Builder() - .forBrowser(WebComponentConfig.browserName) - .withCapabilities(WebComponentConfig.browserCapabilities) - .setChromeOptions(WebComponentConfig.browserOptions) - .usingServer(WebComponentConfig.seleniumAddress) - .build(); - } - - beforeEach(async () => { - browser = await createChromeBrowser(); - utils = new OpenViduComponentsPO(browser); - }); - - afterEach(async () => { - await browser.quit(); - }); - - /** - * TODO - * It only works with OpenVidu PRO because this is a PRO feature - */ - // it('should toggle BACKGROUND panel on prejoin page when VIDEO is MUTED', async () => { - // let element; - // await browser.get(`${url}`); - // element = await utils.waitForElement('#pre-join-container'); - // expect(await utils.isPresent('#pre-join-container')).to.be.true; - - // const backgroundButton = await utils.waitForElement('#background-effects-btn'); - // expect(await utils.isPresent('#background-effects-btn')).to.be.true; - // expect(await backgroundButton.isEnabled()).to.be.true; - // await backgroundButton.click(); - // await browser.sleep(500); - - // await utils.waitForElement('#background-effects-container'); - // expect(await utils.isPresent('#background-effects-container')).to.be.true; - - // element = await utils.waitForElement('#camera-button'); - // expect(await utils.isPresent('#camera-button')).to.be.true; - // expect(await element.isEnabled()).to.be.true; - // await element.click(); - - // await browser.sleep(500); - // element = await utils.waitForElement('#video-poster'); - // expect(await utils.isPresent('#video-poster')).to.be.true; - - // expect(await backgroundButton.isDisplayed()).to.be.true; - // expect(await backgroundButton.isEnabled()).to.be.false; - - // expect(await utils.isPresent('#background-effects-container')).to.be.false; - // }); - - it('should toggle CHAT panel', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkLayoutPresent(); - - const chatButton = await utils.waitForElement('#chat-panel-btn'); - await chatButton.click(); - - await utils.waitForElement('.sidenav-menu'); - await utils.waitForElement('.input-container'); - expect(await utils.isPresent('.input-container')).to.be.true; - - await utils.waitForElement('.messages-container'); - expect(await utils.isPresent('.messages-container')).to.be.true; - - await chatButton.click(); - - expect(await utils.isPresent('.input-container')).to.be.false; - expect(await utils.isPresent('.messages-container')).to.be.false; - }); - - it('should toggle PARTICIPANTS panel', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkLayoutPresent(); - - const participantBtn = await utils.waitForElement('#participants-panel-btn'); - await participantBtn.click(); - - await utils.waitForElement('.sidenav-menu'); - - await utils.waitForElement('.local-participant-container'); - expect(await utils.isPresent('.local-participant-container')).to.be.true; - - await utils.waitForElement('ov-participant-panel-item'); - expect(await utils.isPresent('ov-participant-panel-item')).to.be.true; - - await participantBtn.click(); - - expect(await utils.isPresent('.local-participant-container')).to.be.false; - expect(await utils.isPresent('ov-participant-panel-item')).to.be.false; - }); - - it('should toggle ACTIVITIES panel', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkLayoutPresent(); - - // Get activities button and click into it - const activitiesBtn = await utils.waitForElement('#activities-panel-btn'); - await activitiesBtn.click(); - - await utils.waitForElement('.sidenav-menu'); - await utils.waitForElement('#activities-container'); - expect(await utils.isPresent('#activities-container')).to.be.true; - - await utils.waitForElement('#recording-activity'); - expect(await utils.isPresent('#recording-activity')).to.be.true; - await activitiesBtn.click(); - - expect(await utils.isPresent('#activities-container')).to.be.false; - expect(await utils.isPresent('#recording-activity')).to.be.false; - }); - - it('should toggle SETTINGS panel', async () => { - let element; - await browser.get(`${url}&prejoin=false`); - - await utils.checkLayoutPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Open more options menu - element = await utils.waitForElement('#more-options-btn'); - await element.click(); - - await browser.sleep(500); - - // Checking if mat menu is present - element = await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; - - // Get settings button and click into it - const settingsBtn = await utils.waitForElement('#toolbar-settings-btn'); - await settingsBtn.click(); - - element = await utils.waitForElement('.sidenav-menu'); - expect(await utils.isPresent('#default-settings-panel')).to.be.true; - }); - - it('should switching between PARTICIPANTS and CHAT panels', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - // Open chat panel - const chatButton = await utils.waitForElement('#chat-panel-btn'); - await chatButton.click(); - - await utils.waitForElement('.sidenav-menu'); - expect(await utils.isPresent('.sidenav-menu')).to.be.true; - - await utils.waitForElement('.input-container'); - expect(await utils.isPresent('.input-container')).to.be.true; - - expect(await utils.isPresent('.messages-container')).to.be.true; - - // Open participants panel - const participantBtn = await utils.waitForElement('#participants-panel-btn'); - await participantBtn.click(); - - await utils.waitForElement('.sidenav-menu'); - - expect(await utils.isPresent('.local-participant-container')).to.be.true; - - expect(await utils.isPresent('ov-participant-panel-item')).to.be.true; - - // Switch to chat panel - await chatButton.click(); - - await utils.waitForElement('.sidenav-menu'); - - expect(await utils.isPresent('.input-container')).to.be.true; - - expect(await utils.isPresent('.messages-container')).to.be.true; - - expect(await utils.isPresent('.local-participant-container')).to.be.false; - - expect(await utils.isPresent('ov-participant-panel-item')).to.be.false; - - // Close chat panel - await chatButton.click(); - expect(await utils.getNumberOfElements('.input-container')).equals(0); - expect(await utils.isPresent('messages-container')).to.be.false; - }); - - it('should switching between sections in SETTINGS PANEL', async () => { - let element; - await browser.get(`${url}&prejoin=false`); - - await utils.checkToolbarIsPresent(); - - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); - - // Open more options menu - element = await utils.waitForElement('#more-options-btn'); - await element.click(); - - await browser.sleep(500); - - // Checking if mat menu is present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; - - // Get settings button and click into it - const settingsBtn = await utils.waitForElement('#toolbar-settings-btn'); - await settingsBtn.click(); - - await utils.waitForElement('.sidenav-menu'); - expect(await utils.isPresent('.sidenav-menu')).to.be.true; - - // Check if general section is shown - element = await utils.waitForElement('#general-opt'); - await element.click(); - - expect(await utils.isPresent('ov-nickname-input')).to.be.true; - - // Check if video section is shown - element = await utils.waitForElement('#video-opt'); - await element.click(); - - expect(await utils.isPresent('ov-video-devices-select')).to.be.true; - - // Check if audio section is shown - element = await utils.waitForElement('#audio-opt'); - await element.click(); - - expect(await utils.isPresent('ov-audio-devices-select')).to.be.true; - }); -}); - -describe('Testing CHAT features', () => { - let browser: WebDriver; - let utils: OpenViduComponentsPO; - - async function createChromeBrowser(): Promise { - return await new Builder() - .forBrowser(WebComponentConfig.browserName) - .withCapabilities(WebComponentConfig.browserCapabilities) - .setChromeOptions(WebComponentConfig.browserOptions) - .usingServer(WebComponentConfig.seleniumAddress) - .build(); - } - - beforeEach(async () => { - browser = await createChromeBrowser(); - utils = new OpenViduComponentsPO(browser); - }); - - afterEach(async () => { - await browser.quit(); - }); - - it('should send an url message and converts in a link', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkLayoutPresent(); - - const chatButton = await utils.waitForElement('#chat-panel-btn'); - await chatButton.click(); - - await utils.waitForElement('.sidenav-menu'); - await utils.waitForElement('.input-container'); - expect(await utils.isPresent('.input-container')).to.be.true; - - const input = await utils.waitForElement('#chat-input'); - await input.sendKeys('demos.openvidu.io'); - - await utils.clickOn('#send-btn'); - - await utils.waitForElement('.msg-content a'); - expect(await utils.isPresent('.msg-content a')).to.be.true; - }); -}); - -describe('Testing TOOLBAR features', () => { - let browser: WebDriver; - let utils: OpenViduComponentsPO; - async function createChromeBrowser(): Promise { - return await new Builder() - .forBrowser(WebComponentConfig.browserName) - .withCapabilities(WebComponentConfig.browserCapabilities) - .setChromeOptions(WebComponentConfig.browserOptions) - .usingServer(WebComponentConfig.seleniumAddress) - .build(); - } - - beforeEach(async () => { - browser = await createChromeBrowser(); - utils = new OpenViduComponentsPO(browser); - }); - - afterEach(async () => { - await browser.quit(); - }); - - it('should mute and unmute the local microphone', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkLayoutPresent(); - - const micButton = await utils.waitForElement('#mic-btn'); - await micButton.click(); - - await utils.waitForElement('#mic-btn #mic_off'); - expect(await utils.isPresent('#mic-btn #mic_off')).to.be.true; - - await micButton.click(); - - await utils.waitForElement('#mic-btn #mic'); - expect(await utils.isPresent('#mic-btn #mic')).to.be.true; - }); - - it('should mute and unmute the local camera', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkLayoutPresent(); - - const cameraButton = await utils.waitForElement('#camera-btn'); - await cameraButton.click(); - - await utils.waitForElement('#camera-btn #videocam_off'); - expect(await utils.isPresent('#camera-btn #videocam_off')).to.be.true; - - await cameraButton.click(); - - await utils.waitForElement('#camera-btn #videocam'); - expect(await utils.isPresent('#camera-btn #videocam')).to.be.true; - }); -}); - - -describe('Testing video is playing', () => { - let browser: WebDriver; - let utils: OpenViduComponentsPO; - - async function createChromeBrowser(): Promise { - return await new Builder() - .forBrowser(WebComponentConfig.browserName) - .withCapabilities(WebComponentConfig.browserCapabilities) - .setChromeOptions(WebComponentConfig.browserOptions) - .usingServer(WebComponentConfig.seleniumAddress) - .build(); - } - - beforeEach(async () => { - browser = await createChromeBrowser(); - utils = new OpenViduComponentsPO(browser); - }); - - afterEach(async () => { - await browser.quit(); - }); - - it('should play the participant video with only audio', async () => { - const sessionName = 'audioOnlyE2E'; - const fixedUrl = `${url}&sessionName=${sessionName}`; - await browser.get(fixedUrl); - - await utils.checkPrejoinIsPresent(); - await utils.clickOn('#join-button'); - - // Starting new browser for adding the second participant - const newTabScript = `window.open("${fixedUrl}")`; - await browser.executeScript(newTabScript); - const tabs = await browser.getAllWindowHandles(); - await browser.switchTo().window(tabs[1]); - - await utils.checkPrejoinIsPresent(); - await utils.clickOn('#camera-button'); - await utils.clickOn('#join-button'); - - // Go to first tab - await browser.switchTo().window(tabs[0]); - - // Wait until NO_STREAM_PLAYING_EVENT exception timeout is reached - await browser.sleep(6000); - - const exceptionQuantity = await utils.getNumberOfElements('#NO_STREAM_PLAYING_EVENT'); - expect(exceptionQuantity).equals(0); - }); - - it('should play the participant video with only video', async () => { - const sessionName = 'videoOnlyE2E'; - const fixedUrl = `${url}&sessionName=${sessionName}`; - await browser.get(fixedUrl); - - await utils.checkPrejoinIsPresent(); - await utils.clickOn('#join-button'); - - // Starting new browser for adding the second participant - const newTabScript = `window.open("${fixedUrl}")`; - await browser.executeScript(newTabScript); - const tabs = await browser.getAllWindowHandles(); - await browser.switchTo().window(tabs[1]); - - await utils.checkPrejoinIsPresent(); - await utils.clickOn('#microphone-button'); - await utils.clickOn('#join-button'); - - // Go to first tab - await browser.switchTo().window(tabs[0]); - - // Wait until NO_STREAM_PLAYING_EVENT exception timeout is reached - await browser.sleep(6000); - - const exceptionQuantity = await utils.getNumberOfElements('#NO_STREAM_PLAYING_EVENT'); - expect(exceptionQuantity).equals(0); - }); -}); - -describe('Testing WITHOUT MEDIA DEVICES permissions', () => { - let browser: WebDriver; - let utils: OpenViduComponentsPO; - async function createChromeBrowser(): Promise { - return await new Builder() - .forBrowser(WebComponentConfig.browserName) - .withCapabilities(WebComponentConfig.browserCapabilities) - .setChromeOptions(getBrowserOptionsWithoutDevices()) - .usingServer(WebComponentConfig.seleniumAddress) - .build(); - } - - beforeEach(async () => { - browser = await createChromeBrowser(); - utils = new OpenViduComponentsPO(browser); - }); - - afterEach(async () => { - await browser.quit(); - }); - - it('should be able to ACCESS to PREJOIN page', async () => { - await browser.get(`${url}`); - - await utils.checkPrejoinIsPresent(); - - let button = await utils.waitForElement('#camera-button'); - expect(await button.isEnabled()).to.be.false; - - button = await utils.waitForElement('#microphone-button'); - expect(await button.isEnabled()).to.be.false; - }); - - it('should be able to ACCESS to ROOM page', async () => { - await browser.get(`${url}`); - - await utils.checkPrejoinIsPresent(); - - await utils.clickOn('#join-button'); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - let button = await utils.waitForElement('#camera-btn'); - expect(await button.isEnabled()).to.be.false; - - button = await utils.waitForElement('#mic-btn'); - expect(await button.isEnabled()).to.be.false; - }); - - it('should be able to ACCESS to ROOM page without prejoin', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - let button = await utils.waitForElement('#camera-btn'); - expect(await button.isEnabled()).to.be.false; - - button = await utils.waitForElement('#mic-btn'); - expect(await button.isEnabled()).to.be.false; - }); - - it('should the settings buttons be disabled', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkToolbarIsPresent(); - - // Open more options menu - await utils.clickOn('#more-options-btn'); - - await browser.sleep(500); - - // Checking if fullscreen button is not present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; - - await utils.clickOn('#toolbar-settings-btn'); - - await browser.sleep(500); - - await utils.waitForElement('.settings-container'); - expect(await utils.isPresent('.settings-container')).to.be.true; - - await utils.clickOn('#video-opt'); - expect(await utils.isPresent('ov-video-devices-select')).to.be.true; - - let button = await utils.waitForElement('#camera-button'); - expect(await button.isEnabled()).to.be.false; - - await utils.clickOn('#audio-opt'); - expect(await utils.isPresent('ov-audio-devices-select')).to.be.true; - - button = await utils.waitForElement('#microphone-button'); - expect(await button.isEnabled()).to.be.false; - - }); -}); describe('Testing PRO features with OpenVidu CE', () => { let browser: WebDriver; @@ -1963,363 +28,88 @@ describe('Testing PRO features with OpenVidu CE', () => { await browser.quit(); }); - it('should SHOW the VIRTUAL BACKGROUND PRO feature dialog', async () => { - await browser.get(`${url}&prejoin=true`); + // TODO: Uncomment when background feature is supported + // it('should SHOW the VIRTUAL BACKGROUND PRO feature dialog', async () => { + // await browser.get(`${url}&prejoin=true`); - await utils.checkPrejoinIsPresent(); + // await utils.checkPrejoinIsPresent(); - await utils.waitForElement('#background-effects-btn'); - await utils.clickOn('#background-effects-btn'); + // await utils.waitForElement('#background-effects-btn'); + // await utils.clickOn('#background-effects-btn'); - await utils.chceckProFeatureAlertIsPresent(); + // await utils.chceckProFeatureAlertIsPresent(); - // Close alert - await (await utils.waitForElement('html')).sendKeys(Key.ESCAPE); + // // Close alert + // await (await utils.waitForElement('html')).sendKeys(Key.ESCAPE); - // Join to room - await utils.clickOn('#join-button'); + // // Join to room + // await utils.clickOn('#join-button'); - await utils.checkSessionIsPresent(); + // await utils.checkSessionIsPresent(); - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); + // // Checking if toolbar is present + // await utils.checkToolbarIsPresent(); - // Open more options menu - await utils.clickOn('#more-options-btn'); + // // Open more options menu + // await utils.clickOn('#more-options-btn'); - await browser.sleep(500); + // await browser.sleep(500); - // Checking if button panel is present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; + // // Checking if button panel is present + // await utils.waitForElement('#more-options-menu'); + // expect(await utils.isPresent('#more-options-menu')).to.be.true; - await utils.waitForElement('#virtual-bg-btn'); - await utils.clickOn('#virtual-bg-btn'); + // await utils.waitForElement('#virtual-bg-btn'); + // await utils.clickOn('#virtual-bg-btn'); - // Expect it shows the pro feature alert - await utils.chceckProFeatureAlertIsPresent(); - }); + // // Expect it shows the pro feature alert + // await utils.chceckProFeatureAlertIsPresent(); + // }); - it('should SHOW the CAPTIONS PRO feature dialog', async () => { - await browser.get(`${url}&prejoin=false`); + // it('should SHOW the CAPTIONS PRO feature dialog', async () => { + // await browser.get(`${url}&prejoin=false`); - await utils.checkSessionIsPresent(); + // await utils.checkSessionIsPresent(); - // Checking if toolbar is present - await utils.checkToolbarIsPresent(); + // // Checking if toolbar is present + // await utils.checkToolbarIsPresent(); - // Open more options menu - await utils.clickOn('#more-options-btn'); + // // Open more options menu + // await utils.clickOn('#more-options-btn'); - await browser.sleep(500); + // await browser.sleep(500); - // Checking if button panel is present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; + // // Checking if button panel is present + // await utils.waitForElement('#more-options-menu'); + // expect(await utils.isPresent('#more-options-menu')).to.be.true; - await utils.waitForElement('#toolbar-settings-btn'); - expect(await utils.isPresent('#toolbar-settings-btn')).to.be.true; - await utils.clickOn('#toolbar-settings-btn'); + // await utils.waitForElement('#toolbar-settings-btn'); + // expect(await utils.isPresent('#toolbar-settings-btn')).to.be.true; + // await utils.clickOn('#toolbar-settings-btn'); - // Expect captions panel shows the pro feature content - await utils.waitForElement('#settings-container'); - await utils.clickOn('#captions-opt'); - await browser.sleep(1000); - await utils.waitForElement('.pro-feature'); + // // Expect captions panel shows the pro feature content + // await utils.waitForElement('#settings-container'); + // await utils.clickOn('#captions-opt'); + // await browser.sleep(1000); + // await utils.waitForElement('.pro-feature'); - // Open more options menu - await utils.clickOn('#more-options-btn'); + // // Open more options menu + // await utils.clickOn('#more-options-btn'); - await browser.sleep(500); + // await browser.sleep(500); - // Checking if button panel is present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; + // // Checking if button panel is present + // await utils.waitForElement('#more-options-menu'); + // expect(await utils.isPresent('#more-options-menu')).to.be.true; - // Checking if captions button is present - await utils.waitForElement('#captions-btn'); - expect(await utils.isPresent('#captions-btn')).to.be.true; - await utils.clickOn('#captions-btn'); + // // Checking if captions button is present + // await utils.waitForElement('#captions-btn'); + // expect(await utils.isPresent('#captions-btn')).to.be.true; + // await utils.clickOn('#captions-btn'); - await utils.waitForElement('ov-pro-feature-template'); - expect(await utils.isPresent('.captions-container')).to.be.false; - }); + // await utils.waitForElement('ov-pro-feature-template'); + // expect(await utils.isPresent('.captions-container')).to.be.false; + // }); }); -/** - * TODO: - * The following E2E TESTS only work with OpenVidu PRO. - * It should run with OpenVidu PRO - */ -// describe('Testing captions features', () => { -// let browser: WebDriver; -// let utils: OpenViduComponentsPO; -// async function createChromeBrowser(): Promise { -// return await new Builder() -// .forBrowser(WebComponentConfig.browserName) -// .withCapabilities(WebComponentConfig.browserCapabilities) -// .setChromeOptions(WebComponentConfig.browserOptions) -// .usingServer(WebComponentConfig.seleniumAddress) -// .build(); -// } -// beforeEach(async () => { -// browser = await createChromeBrowser(); -// utils = new OpenViduComponentsPO(browser); -// }); - -// afterEach(async () => { -// await browser.quit(); -// }); - -// it('should OPEN the CAPTIONS container', async () => { -// await browser.get(`${url}&prejoin=false`); - -// await utils.checkSessionIsPresent(); - -// // Checking if toolbar is present -// await utils.checkToolbarIsPresent(); - -// // Open more options menu -// await utils.clickOn('#more-options-btn'); - -// await browser.sleep(500); - -// // Checking if button panel is present -// await utils.waitForElement('.mat-mdc-menu-content'); -// expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; - -// // Checking if captions button is present -// await utils.waitForElement('#captions-btn'); -// expect(await utils.isPresent('#captions-btn')).to.be.true; -// await utils.clickOn('#captions-btn'); - -// await utils.waitForElement('.captions-container'); -// }); - -// it('should OPEN the SETTINGS panel from captions button', async () => { -// await browser.get(`${url}&prejoin=false`); - -// await utils.checkSessionIsPresent(); - -// // Checking if toolbar is present -// await utils.checkToolbarIsPresent(); - -// // Open more options menu -// await utils.clickOn('#more-options-btn'); - -// await browser.sleep(500); - -// // Checking if button panel is present -// await utils.waitForElement('.mat-mdc-menu-content'); -// expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; - -// // Checking if captions button is present -// await utils.waitForElement('#captions-btn'); -// expect(await utils.isPresent('#captions-btn')).to.be.true; -// await utils.clickOn('#captions-btn'); - -// await utils.waitForElement('.captions-container'); -// await utils.waitForElement('#caption-settings-btn'); -// await utils.clickOn('#caption-settings-btn'); - -// await browser.sleep(500); - -// await utils.waitForElement('.settings-container'); -// expect(await utils.isPresent('.settings-container')).to.be.true; - -// await utils.waitForElement('ov-captions-settings'); - -// // Expect caption button is not present -// expect(await utils.isPresent('#caption-settings-btn')).to.be.false; -// }); - -// it('should TOGGLE the CAPTIONS container from settings panel', async () => { -// await browser.get(`${url}&prejoin=false`); - -// await utils.checkSessionIsPresent(); - -// // Checking if toolbar is present -// await utils.checkToolbarIsPresent(); - -// // Open more options menu -// await utils.clickOn('#more-options-btn'); - -// await browser.sleep(500); - -// // Checking if button panel is present -// await utils.waitForElement('.mat-mdc-menu-content'); -// expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; - -// // Checking if captions button is present -// await utils.waitForElement('#captions-btn'); -// expect(await utils.isPresent('#captions-btn')).to.be.true; -// await utils.clickOn('#captions-btn'); - -// await utils.waitForElement('.captions-container'); -// await utils.waitForElement('#caption-settings-btn'); -// await utils.clickOn('#caption-settings-btn'); - -// await browser.sleep(500); - -// await utils.waitForElement('.settings-container'); -// expect(await utils.isPresent('.settings-container')).to.be.true; - -// await utils.waitForElement('ov-captions-settings'); - -// expect(await utils.isPresent('.captions-container')).to.be.true; -// await utils.clickOn('#captions-toggle-slide'); -// expect(await utils.isPresent('.captions-container')).to.be.false; - -// await browser.sleep(200); - -// await utils.clickOn('#captions-toggle-slide'); -// expect(await utils.isPresent('.captions-container')).to.be.true; -// }); - -// it('should change the CAPTIONS language', async () => { -// await browser.get(`${url}&prejoin=false`); - -// await utils.checkSessionIsPresent(); - -// // Checking if toolbar is present -// await utils.checkToolbarIsPresent(); - -// // Open more options menu -// await utils.clickOn('#more-options-btn'); - -// await browser.sleep(500); - -// // Checking if button panel is present -// await utils.waitForElement('.mat-mdc-menu-content'); -// expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; - -// // Checking if captions button is present -// await utils.waitForElement('#captions-btn'); -// expect(await utils.isPresent('#captions-btn')).to.be.true; -// await utils.clickOn('#captions-btn'); - -// await utils.waitForElement('.captions-container'); -// await utils.waitForElement('#caption-settings-btn'); -// await utils.clickOn('#caption-settings-btn'); - -// await browser.sleep(500); - -// await utils.waitForElement('.settings-container'); -// expect(await utils.isPresent('.settings-container')).to.be.true; - -// await utils.waitForElement('ov-captions-settings'); - -// expect(await utils.isPresent('.captions-container')).to.be.true; - -// await utils.clickOn('.lang-button'); -// await browser.sleep(500); - -// await utils.clickOn('#es-ES'); -// await utils.clickOn('.panel-close-button'); - -// const button = await utils.waitForElement('#caption-settings-btn'); -// expect(await button.getText()).equals('settingsEspañol'); - -// }); -// }); - -describe('Testing WITHOUT MEDIA DEVICES permissions', () => { - let browser: WebDriver; - let utils: OpenViduComponentsPO; - async function createChromeBrowser(): Promise { - return await new Builder() - .forBrowser(WebComponentConfig.browserName) - .withCapabilities(WebComponentConfig.browserCapabilities) - .setChromeOptions(getBrowserOptionsWithoutDevices()) - .usingServer(WebComponentConfig.seleniumAddress) - .build(); - } - - beforeEach(async () => { - browser = await createChromeBrowser(); - utils = new OpenViduComponentsPO(browser); - }); - - afterEach(async () => { - await browser.quit(); - }); - - it('should be able to ACCESS to PREJOIN page', async () => { - await browser.get(`${url}`); - - await utils.checkPrejoinIsPresent(); - - let button = await utils.waitForElement('#camera-button'); - expect(await button.isEnabled()).to.be.false; - - button = await utils.waitForElement('#microphone-button'); - expect(await button.isEnabled()).to.be.false; - }); - - it('should be able to ACCESS to ROOM page', async () => { - await browser.get(`${url}`); - - await utils.checkPrejoinIsPresent(); - - await utils.clickOn('#join-button'); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - let button = await utils.waitForElement('#camera-btn'); - expect(await button.isEnabled()).to.be.false; - - button = await utils.waitForElement('#mic-btn'); - expect(await button.isEnabled()).to.be.false; - }); - - it('should be able to ACCESS to ROOM page without prejoin', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkSessionIsPresent(); - - await utils.checkToolbarIsPresent(); - - let button = await utils.waitForElement('#camera-btn'); - expect(await button.isEnabled()).to.be.false; - - button = await utils.waitForElement('#mic-btn'); - expect(await button.isEnabled()).to.be.false; - }); - - it('should the settings buttons be disabled', async () => { - await browser.get(`${url}&prejoin=false`); - - await utils.checkToolbarIsPresent(); - - // Open more options menu - await utils.clickOn('#more-options-btn'); - - await browser.sleep(500); - - // Checking if fullscreen button is not present - await utils.waitForElement('.mat-mdc-menu-content'); - expect(await utils.isPresent('.mat-mdc-menu-content')).to.be.true; - - await utils.clickOn('#toolbar-settings-btn'); - - await browser.sleep(500); - - await utils.waitForElement('.settings-container'); - expect(await utils.isPresent('.settings-container')).to.be.true; - - await utils.clickOn('#video-opt'); - expect(await utils.isPresent('ov-video-devices-select')).to.be.true; - - let button = await utils.waitForElement('#camera-button'); - expect(await button.isEnabled()).to.be.false; - - await utils.clickOn('#audio-opt'); - expect(await utils.isPresent('ov-audio-devices-select')).to.be.true; - - button = await utils.waitForElement('#microphone-button'); - expect(await button.isEnabled()).to.be.false; - }); -}); diff --git a/openvidu-components-angular/generate-docs.sh b/openvidu-components-angular/generate-docs.sh index d7595372..58e0b0aa 100755 --- a/openvidu-components-angular/generate-docs.sh +++ b/openvidu-components-angular/generate-docs.sh @@ -8,9 +8,9 @@ fi # Replace version from "stable" to the specified one in all TypeDoc links grep -rl '/en/stable/' projects src | xargs sed -i -e 's|/en/stable/|/en/'${BASEHREF_VERSION}'/|g' -# Replace testapp README by openvidu-angular README +# Replace testapp README by openvidu-components-angular README mv README.md README-testapp.md -cp ./projects/openvidu-angular/README.md . +cp ./projects/openvidu-components-angular/README.md . # Generate Compodoc npm run doc:build diff --git a/openvidu-components-angular/migration-guide.md b/openvidu-components-angular/migration-guide.md new file mode 100644 index 00000000..d0c536cd --- /dev/null +++ b/openvidu-components-angular/migration-guide.md @@ -0,0 +1,280 @@ +## OpenVidu Angular 3.0.0 Migration Guide + +1. **Introduction** + +Mejoras: + +publicacion de multiples tracks por participante + +Desventajas + +Backgrounds no existe en openvidu 3.0.0 +Captions no existe en openvidu 3.0.0 + +2. **Updating Dependencies** + +OpenVidu Components 2.x was developed using Angular 14. Now, this version 3 is developed with Angular 15. This means that you will need to update your Angular version to 15.0.0 or higher. Check Angular Migration Guide for more information: https://update.angular.io/ + +Install the new version of OpenVidu Components: + +```bash +npm install openvidu-components-angular@3.0.0 +``` + +3. **Source Code Migration** + + #### Components + + - #### ActivitiesPanelComponent (`ov-activities-panel`): + + ##### Events / `@Output` + + ##### API Directives / `@Input` + + - #### BroadcastingActivityComponent (`ov-broadcasting-activity`): + + ##### Events / `@Output` + + - `onBroadcastingStartRequested` (Describe onBroadcastingStartRequested event here) + - `onRecordingPlayClicked` (Describe onRecordingPlayClicked event here) + + ##### API Directives / `@Input` + + - `broadcastingError` has been deleted + + - #### ChatPanelComponent (`ov-chat-panel`): + + ##### Events / `@Output` + + ##### API Directives / `@Input` + + - #### LayoutComponent (`ov-layout`): + + ##### Events / `@Output` + + ##### API Directives / `@Input` + + - #### PanelComponent (`ov-panel`): + + ##### Events / `@Output` + + ##### API Directives / `@Input` + + - #### ParticipantPanelItemComponent (`ov-participant-panel-item`): + + ##### Events / `@Output` + + ##### API Directives / `@Input` + + - #### ParticipantsPanelComponent (`ov-participants-panel`): + + ##### Events / `@Output` + + ##### API Directives / `@Input` + + - #### RecordingActivityComponent (`ov-recording-activity`): + + ##### Events / `@Output` + + - `onRecordingStartRequested` (Describe onRecordingStartRequested event here) + - `onRecordingStopRequested` (Describe onRecordingStopRequested event here) + + ##### API Directives / `@Input` + + - `recordingError` has been deleted + - `recordingsList` has been deleted + + - #### StreamComponent (`ov-stream`): + + ##### Events / `@Output` + + ##### API Directives / `@Input` + + - `settingsButton` has been replaced by `videoControls` + - `stream` input has been replaced by `track` input. Check the [Pipes](#pipes) section for more information. + + - #### ToolbarComponent (`ov-toolbar`): + + ##### Events / `@Output` + + - `backgroundEffectsButton` has been deleted **TODO: Try to add it again!!** + - `captionsButton` has been deleted + - `displaySessionName` has been renamed to `displayRoomName` + + ##### API Directives / `@Input` + + - #### VideoconferenceComponent (`ov-videoconference`): + + ##### Events / `@Output` + + - `onActivitiesPanelDeleteRecordingClicked` has been replaced by `onRecordingDeleteRequested` + - `onActivitiesPanelForceRecordingUpdate` has been deleted + - `onActivitiesPanelPlayRecordingClicked` has been replaced by `onRecordingPlayClicked` + - `onActivitiesPanelStartBroadcastingClicked` have been replaced by `onBroadcastingStartRequested` + - `onActivitiesPanelStartRecordingClicked` and `onToolbarStartRecordingClicked` has been replaced by `onRecordingStartRequested` + - `onActivitiesPanelStopBroadcastingClicked` and `onToolbarStopBroadcastingClicked` has been replaced by `onBroadcastingStopRequested` + - `onActivitiesPanelStopRecordingClicked` and `onToolbarStopRecordingClicked` has been replaced by `onRecordingStopRequested` + - **// TODO: langChanged** + - `onNodeCrashed` has been deleted + - `onSessionCreated` has been replaced by `onRoomCreated` + - `onToolbarActivitiesPanelButtonClicked` has been replaced by `onActivitiesPanelStatusChanged` + - `onToolbarCameraButtonClicked` has been replaced by `onVideoEnabledChanged` + - `onToolbarChatPanelButtonClicked` has been replaced by `onChatPanelStatusChanged` + - `onToolbarFullscreenButtonClicked` has been replaced by `onFullscreenEnabledChanged` + - `onToolbarLeaveButtonClicked` has been replaced by `onRoomDisconnected` + - `onToolbarMicrophoneButtonClicked` has been replaced by `onAudioEnabledChanged` + - `onToolbarParticipantsPanelButtonClicked` has been replaced by `onParticipantsPanelStatusChanged` + - `onToolbarScreenshareButtonClicked` has been replaced by `onScreenShareEnabledChanged` + - `onSettingsPanelStatusChanged` has been added to notify when the settings panel status has changed + - `onTokenRequested` has been added to notify when a token need to be created + - `onVideoDeviceChanged` has been added to notify when the video device has changed + - `onAudioDeviceChanged` has been added to notify when the audio device has changed + - `onRecordingDownloadClicked` has been added to notify when the user clicks on the download button of a recording + - `onActivitiesPanelPlayRecordingClicked` has been replaced by `onRecordingPlayClicked` + + - #### Admin Login (`ov-admin-login`): + - `onLoginButtonClicked` returns an object with the username and password instead of a secret string + + ##### API Directives / `@Input` + + - `audioMuted` has been replaced by `audioEnabled` + - `broadcastingActivityBroadcastingError` has been deleted + - `captionsLang` has been deleted + - `captionsLangOptions` has been deleted + - `recordingActivityRecordingError` has been deleted + - `recordingActivityRecordingsList` has been deleted + - `streamSettingsButton` has been replaced by `streamVideoControls` + - `toolbarBackgroundEffectsButton` has been deleted **TODO: Try to add it again!!** + - `toolbarCaptionsButton` has been deleted + - `toolbarDisplaySessionName` has been renamed to `toolbarDisplayRoomName` + - `videoMuted` has been replaced by `videoEnabled` + + #### Web Component + + Apply the same Outputs and Inputs changes as in the VideoconferenceComponent + + #### Pipes + + - `stream` has been replaced by `track` + + In version 2.X of openvidu-angular, the **ParticipantStreamsPipe** (`streams`) was employed to extract streams from both the local participant and remote participants' lists. Its purpose was to inject these streams into the **StreamComponent** (`ov-stream`). + + However, in the current version (3.0.0), **StreamComponent** now requires direct access to participant tracks. Consequently, the `streams` pipe has been replaced by the **RemoteParticipantTracksPipe** (`tracks`), exclusively used for remote participants' lists. Local participant tracks can now be obtained directly from the track accessor of the `ParticipantModel` class. This accessor and pipe return an array of [`ParticipantTrackPublication`](#/interfaces/ParticipantTrackPublication.html) objects, simplifying the handling of tracks. + + ##### In v2.X (using `streams` pipe): + + ```html +
+ +
+
+ +
+ ``` + + ##### In v3.0.0 (using `tracks` pipe): + + ```html +
+ +
+
+ +
+ ``` + + #### Services + + - ##### Broadcasting Service (`BroadcastingService`): + + - `broadcastingStatusObs` observable now pushes an [BroadcastingStatusInfo](#/interfaces/BroadcastingStatusInfo.html) object instead of an `BroadcastingStatus` in every update. + - `startBroadcasting` method has been replaced by `setBroadcastingStarting` + - `stopBroadcasting` method has been replaced by `setBroadcastingStopping` + - `updateStatus` method has been deleted. Now the status is updated automatically when the status changes. + + You can check the [BroadcastingService](#/services/BroadcastingService.html) documentation for more information. + + - ##### OpenVidu Service (`OpenViduService`): + + - `disconnect` method has been renamed to `disconnectRoom` + - `getRemoteConnections` method has been moved to [ParticipantService](#participant-service-participantservice) and renamed to `getRemoteParticipants`. + - `getSession` method has been renamed to `getRoom`. + - `isSessionConnected` method has been renamed to `isRoomConnected`. + - `publishCamera` and `unpublishCamera` methods have been moved to [ParticipantService](#participant-service-participantservice) and renamed to `setCameraEnabled` + - `publishScreen` and `unpublishScreen` methods have been renamed to `setScreenShareEnabled` + - `isSttReadyObs` observable has been deleted + - `connectRoom` method has been added + - `getRoomMetadata` method has been added to get the metadata of the room + - `getRoomName` method has been added to get the name of the room + + - ##### Panel Service (`PanelService`): + + - `panelOpenedObs` observable has been renamed to `panelStatusObs` which returns a [PanelStatusInfo](#/interfaces/PanelStatusInfo.html) object instead of a `PanelEvent` in every update. + - `isBackgroundEffectsPanelOpened` method has been deleted + + You can check the [PanelService](#/services/PanelService.html) documentation for more information. + + - ##### Participant Service (`ParticipantService`): + + - `amIModerator` method has been renamed to `amIRoomAdmin` + - `toggleScreenshare` method has been renamed to `switchScreenShare` + - `isMyVideoActive` method has been renamed to `isMyCameraEnabled` + - `isMyAudioActive` method has been renamed to `isMyMicrophoneEnabled` + - `publishVideo` method has been renamed to `setCameraEnabled` + - `publishAudio` method has been renamed to `setMicrophoneEnabled` + + You can check the [ParticipantService](#/services/ParticipantService.html) documentation for more information. + + - ##### Recording Service (`RecordingService`): + + - `recordingStatusObs` observable now pushes an [RecordingStatusInfo](#/interfaces/RecordingStatusInfo.html) object instead of an `RecordingStatus` in every update. + - `forceUpdateRecordings` method has been deleted. Now the recordings are updated automatically when the status changes. + - `updateStatus` method has been deleted. Now the status is updated automatically when the status changes. + + You can check the [RecordingService](#/services/RecordingService.html) documentation for more information. + + #### Models + + - `ParticipantAbstracModel` has been renamed to `ParticipantModel`. In addition, the `ParticipantModel` class has been refactored and now it has a new property called `tracks` that contains all the tracks of the participant. This property is an array of `ParticipantTrackPublication` objects. + + - `StreamModel` has been renamed to `ParticipantTrackPublication`. + + #### Interfaces + + - `BroadcastingError` has been deleted + - `CaptionsLangOptions` has been deleted + - `PanelEvent` has been renamed to `PanelStatusInfo` + - `StreamModel` has been renamed to `ParticipantTrackPublication` + - `TokenModel` has been deleted + +4. **References and Additional Resources** + +5. **FAQ (Frequently Asked Questions)** + + #### **Why should I migrate to version 3.0.0 of OpenVidu Angular?** + + **TODO** + + **Which versions of Angular are compatible with OpenVidu Angular 3.0.0?** + + OpenVidu Angular 3.0.0 is compatible with Angular 15 and later versions. Make sure to update your Angular project to version 15 or higher to use this version of OpenVidu Angular. + + #### **How can I check the current version of Angular in my project?** + + You can check the current version of Angular in your project by running the following command in your terminal: + + ``` + ng version + ``` + + This will provide you with detailed information about the version of Angular you are using in your project. + + #### **Where can I find examples of using the new features and API changes?** + + You can find examples of using the new features and API changes in the official documentation of OpenVidu Angular 3.0.0. Visit the [official tutorials](#). + + #### **How can I contribute or report issues with the new version?** + + If you wish to contribute to the development of OpenVidu Angular or report issues, you can do so through the collaborative development platform, such as GitHub. Visit the official repository at [OpenVidu Angular repository](#) for information on how to contribute and how to report issues. Your participation is valuable in improving the library and assisting other users. + +6. **Support and Community** diff --git a/openvidu-components-angular/openvidu-webcomponent-build.js b/openvidu-components-angular/openvidu-webcomponent-build.js index 6e16ded1..1e6dba97 100644 --- a/openvidu-components-angular/openvidu-webcomponent-build.js +++ b/openvidu-components-angular/openvidu-webcomponent-build.js @@ -3,7 +3,6 @@ const concat = require('concat'); const VERSION = require('./package.json').version; const ovWebcomponentRCPath = './dist/openvidu-webcomponent-rc'; const ovWebcomponentProdPath = './dist/openvidu-webcomponent'; -const bundleName = 'openvidu-webcomponent-v2compatibility-' + VERSION; module.exports.buildWebcomponent = async () => { console.log('Building OpenVidu Web Component (' + VERSION + ')'); @@ -17,7 +16,7 @@ module.exports.buildWebcomponent = async () => { await copyFiles(e2eWcPath); await renameWebComponentTestName(e2eWcPath); - console.log(`OpenVidu Web Component V2 Compatibility (${VERSION}) built`); + console.log(`OpenVidu Web Component (${VERSION}) built`); } catch (error) { console.error(error); } @@ -28,21 +27,21 @@ async function buildElement() { try { await fs.ensureDir('./dist/openvidu-webcomponent'); - await concat(files, `${ovWebcomponentProdPath}/${bundleName}.js`); - await fs.copy(`${ovWebcomponentRCPath}/styles.css`, `${ovWebcomponentProdPath}/${bundleName}.css`); + await concat(files, `${ovWebcomponentProdPath}/openvidu-webcomponent-${VERSION}.js`); + await fs.copy(`${ovWebcomponentRCPath}/styles.css`, `${ovWebcomponentProdPath}/openvidu-webcomponent-${VERSION}.css`); // await fs.copy( // "./dist/openvidu-webcomponent/assets", // "./openvidu-webcomponent/assets" // ); } catch (err) { - console.error('Error executing buildElement function in openvidu-webcomponent-builds.js'); + console.error('Error executing build function in webcomponent-builds.js'); throw err; } } function renameWebComponentTestName(dir) { - fs.renameSync(`${dir}/${bundleName}.js`, `${dir}/openvidu-webcomponent-dev.js`); - fs.renameSync(`${dir}/${bundleName}.css`, `${dir}/openvidu-webcomponent-dev.css`); + fs.renameSync(`${dir}/openvidu-webcomponent-${VERSION}.js`, `${dir}/openvidu-webcomponent-dev.js`); + fs.renameSync(`${dir}/openvidu-webcomponent-${VERSION}.css`, `${dir}/openvidu-webcomponent-dev.css`); } async function copyFiles(destination) { diff --git a/openvidu-components-angular/package-lock.json b/openvidu-components-angular/package-lock.json index 64536f9b..724dbb45 100644 --- a/openvidu-components-angular/package-lock.json +++ b/openvidu-components-angular/package-lock.json @@ -1,47 +1,52 @@ { "name": "openvidu-components-testapp", - "version": "3.0.0-beta1", + "version": "3.0.0-dev", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "openvidu-components-testapp", - "version": "3.0.0-beta1", + "version": "3.0.0-dev", "dependencies": { - "@angular/animations": "17.3.10", - "@angular/cdk": "17.3.10", - "@angular/common": "17.3.10", - "@angular/core": "17.3.10", - "@angular/flex-layout": "15.0.0-beta.42", - "@angular/forms": "17.3.10", - "@angular/material": "17.3.10", - "@angular/platform-browser": "17.3.10", - "@angular/platform-browser-dynamic": "17.3.10", - "@angular/router": "17.3.10", + "@angular/animations": "18.0.0", + "@angular/cdk": "18.0.0", + "@angular/common": "18.0.0", + "@angular/core": "18.0.0", + "@angular/forms": "18.0.0", + "@angular/material": "18.0.0", + "@angular/platform-browser": "18.0.0", + "@angular/platform-browser-dynamic": "18.0.0", + "@angular/router": "18.0.0", + "@livekit/track-processors": "0.3.1", "autolinker": "4.0.0", - "openvidu-browser-v2compatibility": "^3.0.0-dev5", + "livekit-client": "2.1.0", "rxjs": "7.5.7", "tslib": "2.3.1", "zone.js": "^0.14.6" }, "devDependencies": { - "@angular-devkit/build-angular": "17.3.8", - "@angular/cli": "17.3.8", - "@angular/compiler": "17.3.10", - "@angular/compiler-cli": "17.3.10", - "@angular/elements": "17.3.10", - "@compodoc/compodoc": "^1.1.19", - "@types/chai": "4.3.0", + "@angular-devkit/build-angular": "18.0.1", + "@angular/cli": "18.0.1", + "@angular/compiler": "18.0.0", + "@angular/compiler-cli": "18.0.0", + "@angular/elements": "18.0.0", + "@compodoc/compodoc": "^1.1.25", + "@types/chai": "4.3.6", + "@types/dom-mediacapture-transform": "0.1.9", + "@types/dom-webcodecs": "0.1.11", "@types/mocha": "9.1.0", - "@types/node": "20.12.12", - "@types/selenium-webdriver": "4.1.5", - "@types/ws": "8.5.4", + "@types/node": "20.12.7", + "@types/selenium-webdriver": "4.1.16", + "@types/ws": "^8.5.4", "chai": "4.3.6", - "chromedriver": "122.0.5", + "chromedriver": "116.0.0", "codelyzer": "6.0.2", "concat": "^1.0.3", "cross-env": "^7.0.3", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", "http-server": "14.1.1", + "husky": "^8.0.3", "jasmine-core": "3.10.1", "jasmine-spec-reporter": "7.0.0", "karma": "^6.3.9", @@ -53,12 +58,15 @@ "karma-junit-reporter": "2.0.1", "karma-mocha-reporter": "2.2.5", "karma-notify-reporter": "1.3.0", + "lint-staged": "^14.0.1", "mocha": "9.2.2", - "ng-packagr": "17.3.0", - "selenium-webdriver": "4.5.0", + "ng-packagr": "18.0.0", + "npm-watch": "^0.11.0", + "prettier": "3.0.3", + "selenium-webdriver": "4.12.0", "ts-node": "10.4.0", "tslint": "6.1.3", - "typescript": "^5.4.5", + "typescript": "5.4.5", "webpack-bundle-analyzer": "^4.5.0" } }, @@ -82,16 +90,16 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1703.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1703.8.tgz", - "integrity": "sha512-lKxwG4/QABXZvJpqeSIn/kAwnY6MM9HdHZUV+o5o3UiTi+vO8rZApG4CCaITH3Bxebm7Nam7Xbk8RuukC5rq6g==", + "version": "0.1800.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1800.1.tgz", + "integrity": "sha512-L3n1Rh0NUNTlQZBBuPY8VFc5Skr6Oa6xT821k+XLLZTbz1ci2e3ltINyUhqISeksa3AyyL8e4JR2kCbDli9uJA==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.3.8", + "@angular-devkit/core": "18.0.1", "rxjs": "7.8.1" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } @@ -106,97 +114,98 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.3.8.tgz", - "integrity": "sha512-ixsdXggWaFRP7Jvxd0AMukImnePuGflT9Yy7NJ9/y0cL/k//S/3RnkQv5i411KzN+7D4RIbNkRGGTYeqH24zlg==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.0.1.tgz", + "integrity": "sha512-FDVxR+VR0WP/lukOrnhEdy+hcGNBzqyfmrW0fyIthwP+A/gHlB3Qd/lehkeLngTjPwtBXssxuwR6BgWmpjy69Q==", "dev": true, "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1703.8", - "@angular-devkit/build-webpack": "0.1703.8", - "@angular-devkit/core": "17.3.8", - "@babel/core": "7.24.0", - "@babel/generator": "7.23.6", + "@angular-devkit/architect": "0.1800.1", + "@angular-devkit/build-webpack": "0.1800.1", + "@angular-devkit/core": "18.0.1", + "@angular/build": "18.0.1", + "@babel/core": "7.24.5", + "@babel/generator": "7.24.5", "@babel/helper-annotate-as-pure": "7.22.5", - "@babel/helper-split-export-declaration": "7.22.6", - "@babel/plugin-transform-async-generator-functions": "7.23.9", - "@babel/plugin-transform-async-to-generator": "7.23.3", - "@babel/plugin-transform-runtime": "7.24.0", - "@babel/preset-env": "7.24.0", - "@babel/runtime": "7.24.0", + "@babel/helper-split-export-declaration": "7.24.5", + "@babel/plugin-transform-async-generator-functions": "7.24.3", + "@babel/plugin-transform-async-to-generator": "7.24.1", + "@babel/plugin-transform-runtime": "7.24.3", + "@babel/preset-env": "7.24.5", + "@babel/runtime": "7.24.5", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "17.3.8", + "@ngtools/webpack": "18.0.1", "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", - "autoprefixer": "10.4.18", + "autoprefixer": "10.4.19", "babel-loader": "9.1.3", "babel-plugin-istanbul": "6.1.1", "browserslist": "^4.21.5", "copy-webpack-plugin": "11.0.0", "critters": "0.0.22", - "css-loader": "6.10.0", - "esbuild-wasm": "0.20.1", + "css-loader": "7.1.1", + "esbuild-wasm": "0.21.3", "fast-glob": "3.3.2", - "http-proxy-middleware": "2.0.6", + "http-proxy-middleware": "3.0.0", "https-proxy-agent": "7.0.4", - "inquirer": "9.2.15", + "inquirer": "9.2.22", "jsonc-parser": "3.2.1", "karma-source-map-support": "1.4.0", "less": "4.2.0", - "less-loader": "11.1.0", + "less-loader": "12.2.0", "license-webpack-plugin": "4.0.2", "loader-utils": "3.2.1", - "magic-string": "0.30.8", - "mini-css-extract-plugin": "2.8.1", + "magic-string": "0.30.10", + "mini-css-extract-plugin": "2.9.0", "mrmime": "2.0.0", "open": "8.4.2", "ora": "5.4.1", "parse5-html-rewriting-stream": "7.0.0", - "picomatch": "4.0.1", - "piscina": "4.4.0", - "postcss": "8.4.35", + "picomatch": "4.0.2", + "piscina": "4.5.0", + "postcss": "8.4.38", "postcss-loader": "8.1.1", "resolve-url-loader": "5.0.0", "rxjs": "7.8.1", - "sass": "1.71.1", - "sass-loader": "14.1.1", - "semver": "7.6.0", + "sass": "1.77.2", + "sass-loader": "14.2.1", + "semver": "7.6.2", "source-map-loader": "5.0.0", "source-map-support": "0.5.21", - "terser": "5.29.1", + "terser": "5.31.0", "tree-kill": "1.2.2", "tslib": "2.6.2", - "undici": "6.11.1", - "vite": "5.1.7", - "watchpack": "2.4.0", - "webpack": "5.90.3", - "webpack-dev-middleware": "6.1.2", - "webpack-dev-server": "4.15.1", + "undici": "6.18.0", + "vite": "5.2.11", + "watchpack": "2.4.1", + "webpack": "5.91.0", + "webpack-dev-middleware": "7.2.1", + "webpack-dev-server": "5.0.4", "webpack-merge": "5.10.0", "webpack-subresource-integrity": "5.1.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "optionalDependencies": { - "esbuild": "0.20.1" + "esbuild": "0.21.3" }, "peerDependencies": { - "@angular/compiler-cli": "^17.0.0", - "@angular/localize": "^17.0.0", - "@angular/platform-server": "^17.0.0", - "@angular/service-worker": "^17.0.0", + "@angular/compiler-cli": "^18.0.0", + "@angular/localize": "^18.0.0", + "@angular/platform-server": "^18.0.0", + "@angular/service-worker": "^18.0.0", "@web/test-runner": "^0.18.0", "browser-sync": "^3.0.2", "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "karma": "^6.3.0", - "ng-packagr": "^17.0.0", + "ng-packagr": "^18.0.0", "protractor": "^7.0.0", "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=5.2 <5.5" + "typescript": ">=5.4 <5.5" }, "peerDependenciesMeta": { "@angular/localize": { @@ -234,6 +243,74 @@ } } }, + "node_modules/@angular-devkit/build-angular/node_modules/@angular/build": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.0.1.tgz", + "integrity": "sha512-n2So6inJ4Prw3NOPC6keyVyFDryFNCJ4UUzmjtPOS8FyYqThWBcuXFzsUsUCFbXSUqVBZh9vxEqHqggnCAs9Og==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.1800.1", + "@babel/core": "7.24.5", + "@babel/helper-annotate-as-pure": "7.22.5", + "@babel/helper-split-export-declaration": "7.24.5", + "@vitejs/plugin-basic-ssl": "1.1.0", + "ansi-colors": "4.1.3", + "browserslist": "^4.23.0", + "critters": "0.0.22", + "esbuild": "0.21.3", + "fast-glob": "3.3.2", + "https-proxy-agent": "7.0.4", + "inquirer": "9.2.22", + "lmdb": "3.0.8", + "magic-string": "0.30.10", + "mrmime": "2.0.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "7.0.0", + "picomatch": "4.0.2", + "piscina": "4.5.0", + "sass": "1.77.2", + "semver": "7.6.2", + "undici": "6.18.0", + "vite": "5.2.11", + "watchpack": "2.4.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^18.0.0", + "@angular/localize": "^18.0.0", + "@angular/platform-server": "^18.0.0", + "@angular/service-worker": "^18.0.0", + "less": "^4.2.0", + "postcss": "^8.4.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=5.4 <5.5" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "less": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tailwindcss": { + "optional": true + } + } + }, "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -250,22 +327,22 @@ "dev": true }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1703.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1703.8.tgz", - "integrity": "sha512-9u6fl8VVOxcLOEMzrUeaybSvi9hSLSRucHnybneYrabsgreDo32tuy/4G8p6YAHQjpWEj9jvF9Um13ertdni5Q==", + "version": "0.1800.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1800.1.tgz", + "integrity": "sha512-a5/0mOBRgrQZVv2yc0TXlnwb5etil6Wb/T44tXh0EHsOeaKXGCqWQPVu1EjVJoHieVdXOcajGrPo0aGd8blsdg==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1703.8", + "@angular-devkit/architect": "0.1800.1", "rxjs": "7.8.1" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "peerDependencies": { "webpack": "^5.30.0", - "webpack-dev-server": "^4.0.0" + "webpack-dev-server": "^5.0.2" } }, "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { @@ -278,20 +355,20 @@ } }, "node_modules/@angular-devkit/core": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.8.tgz", - "integrity": "sha512-Q8q0voCGudbdCgJ7lXdnyaxKHbNQBARH68zPQV72WT8NWy+Gw/tys870i6L58NWbBaCJEUcIj/kb6KoakSRu+Q==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.0.1.tgz", + "integrity": "sha512-91eKZoObs+wRgwssw81Y/94Nvixj0WqJkNusBAg+gAfZTCEeJoGGZJkRK8wrONbM79C3Bx8lN/TfSIPRbjnfOQ==", "dev": true, "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", + "ajv": "8.13.0", + "ajv-formats": "3.0.1", "jsonc-parser": "3.2.1", - "picomatch": "4.0.1", + "picomatch": "4.0.2", "rxjs": "7.8.1", "source-map": "0.7.4" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, @@ -314,19 +391,19 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.8.tgz", - "integrity": "sha512-QRVEYpIfgkprNHc916JlPuNbLzOgrm9DZalHasnLUz4P6g7pR21olb8YCyM2OTJjombNhya9ZpckcADU5Qyvlg==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.0.1.tgz", + "integrity": "sha512-AKcEGa3fIgyXT6XTQZWEJZzgmcqlB89fcF7JFOuz4rgQfRmnE2xFw37lKE6ZclCOSiEoffAvgrL8acjdPI1ouw==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.3.8", + "@angular-devkit/core": "18.0.1", "jsonc-parser": "3.2.1", - "magic-string": "0.30.8", + "magic-string": "0.30.10", "ora": "5.4.1", "rxjs": "7.8.1" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } @@ -341,9 +418,9 @@ } }, "node_modules/@angular/animations": { - "version": "17.3.10", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.3.10.tgz", - "integrity": "sha512-9fR5snTuG4aM2K54TG/6DXcKXMDKZMovZhjQOxO8l68/oqn6fKrHs8DLzckFs0XGRZ+2OyURH8WggFm1Z828rA==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.0.0.tgz", + "integrity": "sha512-An/IqDBCyWZXVC23+jRKdmvJB/b4P1BVljZxGxF+CiocNd/xvVVeBYuuxzp3vhhVobyO8A9iD12itPudLOpt2Q==", "dependencies": { "tslib": "^2.3.0" }, @@ -351,13 +428,13 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/core": "17.3.10" + "@angular/core": "18.0.0" } }, "node_modules/@angular/cdk": { - "version": "17.3.10", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-17.3.10.tgz", - "integrity": "sha512-b1qktT2c1TTTe5nTji/kFAVW92fULK0YhYAvJ+BjZTPKu2FniZNe8o4qqQ0pUuvtMu+ZQxp/QqFYoidIVCjScg==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.0.0.tgz", + "integrity": "sha512-V0i1SAiT2PTNyugBW0E4fev8G/4XP5FdyX2YD6oc5sNyt3GFcoDNHcz+oEne8+aYVnQ3Ax9Zutq/SQincDHIbw==", "dependencies": { "tslib": "^2.3.0" }, @@ -365,33 +442,32 @@ "parse5": "^7.1.2" }, "peerDependencies": { - "@angular/common": "^17.0.0 || ^18.0.0", - "@angular/core": "^17.0.0 || ^18.0.0", + "@angular/common": "^18.0.0 || ^19.0.0", + "@angular/core": "^18.0.0 || ^19.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/cli": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-17.3.8.tgz", - "integrity": "sha512-X5ZOQ6ZTKVHjhIsfl32ZRqbs+FUoeHLbT7x4fh2Os/8ObDDwrUcCJPqxe2b2RB5E2d0vepYigknHeLE7gwzlNQ==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.0.1.tgz", + "integrity": "sha512-O1kQOxXsfxHgGyqdHc2OTwlUTXLE8O1UcGkWROxvKt4MXccdJLjMjypMiV+jSpzc0FJTV1ihSkCxMtBezF926A==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1703.8", - "@angular-devkit/core": "17.3.8", - "@angular-devkit/schematics": "17.3.8", - "@schematics/angular": "17.3.8", + "@angular-devkit/architect": "0.1800.1", + "@angular-devkit/core": "18.0.1", + "@angular-devkit/schematics": "18.0.1", + "@schematics/angular": "18.0.1", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.3", "ini": "4.1.2", - "inquirer": "9.2.15", + "inquirer": "9.2.22", "jsonc-parser": "3.2.1", - "npm-package-arg": "11.0.1", - "npm-pick-manifest": "9.0.0", - "open": "8.4.2", + "npm-package-arg": "11.0.2", + "npm-pick-manifest": "9.0.1", "ora": "5.4.1", - "pacote": "17.0.6", + "pacote": "18.0.6", "resolve": "1.22.8", - "semver": "7.6.0", + "semver": "7.6.2", "symbol-observable": "4.0.0", "yargs": "17.7.2" }, @@ -399,15 +475,15 @@ "ng": "bin/ng.js" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, "node_modules/@angular/common": { - "version": "17.3.10", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-17.3.10.tgz", - "integrity": "sha512-6SfD21M3LujymmZsZQIxAsV8Bj5u6He6ImZ+p2rr7FAhFxpVJyKldK8LCmJcFsBD4srpQcxEZ0iDxXvg+0ihAw==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.0.0.tgz", + "integrity": "sha512-s43ZcOhXTUlkdOPMiMtr4Pz1qKIS8nClXhaahY0JBQZYGsOSn7NR42SoEeB8/ixktfY60s3SLhizXTKMAYtOTA==", "dependencies": { "tslib": "^2.3.0" }, @@ -415,14 +491,14 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/core": "17.3.10", + "@angular/core": "18.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "17.3.10", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.3.10.tgz", - "integrity": "sha512-6Ce4siHyF0fCZBDm/cz+blJByGDu1/hbPkQVGmk5HGZTmCUeKkgyjoM6bZr7ssAsyGDRwxBh2SGHO4Ce31vuPA==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.0.0.tgz", + "integrity": "sha512-KbyjUfpdVE8+6fiHqo4PgVrGppYUhlU1JVAj6dqeUug9lQ5HBcANfiZ7p8CA2lU3gvIZ1cj+ZDKA1NEB1wvvtQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -430,7 +506,7 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/core": "17.3.10" + "@angular/core": "18.0.0" }, "peerDependenciesMeta": { "@angular/core": { @@ -439,12 +515,12 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "17.3.10", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.3.10.tgz", - "integrity": "sha512-85SBphqRj3szac3FbeYgEZ+I6WaAlo5h7JX06BdjOLLiaoIwlFhLeAuG+jVekseV+95grFUxIsCMphWHi2e6hQ==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.0.0.tgz", + "integrity": "sha512-fy9MBSHDM/YAyrIWa15JV1ZrpuSc51HHUSA3W/UKrDqUqSfYyj11/0PeYkdIWUD/dACZSrEge3nVnYCjdyJqPA==", "dev": true, "dependencies": { - "@babel/core": "7.23.9", + "@babel/core": "7.24.4", "@jridgewell/sourcemap-codec": "^1.4.14", "chokidar": "^3.0.0", "convert-source-map": "^1.5.1", @@ -462,26 +538,26 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/compiler": "17.3.10", - "typescript": ">=5.2 <5.5" + "@angular/compiler": "18.0.0", + "typescript": ">=5.4 <5.5" } }, "node_modules/@angular/compiler-cli/node_modules/@babel/core": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", - "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", + "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.4", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.9", - "@babel/parser": "^7.23.9", - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9", + "@babel/helpers": "^7.24.4", + "@babel/parser": "^7.24.4", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -512,9 +588,9 @@ } }, "node_modules/@angular/core": { - "version": "17.3.10", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-17.3.10.tgz", - "integrity": "sha512-ocEKu7X0yFCOvgJn1uZy76qjhsjKvULrO1k/BuIX0nwhp61DTGYTvCqKmwCBLM8/gvcKYH5vMKMHoQKtiSGE0A==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.0.0.tgz", + "integrity": "sha512-tpR7HIY4MJuM9ETpG15IvBr1wsI8Cyec3ZxYFe/27FKHARvxDbqIrT9QevmC6lxg1NdfD990G2XphYML1EyJ8g==", "dependencies": { "tslib": "^2.3.0" }, @@ -527,9 +603,9 @@ } }, "node_modules/@angular/elements": { - "version": "17.3.10", - "resolved": "https://registry.npmjs.org/@angular/elements/-/elements-17.3.10.tgz", - "integrity": "sha512-rARoZx0wBlouAQot2ItD0h0gCKbKfa43S2545wiqcIVxrkxYMhyX54GDbUv8zQzd7YBUguITeGjaIC9hKFLeqQ==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@angular/elements/-/elements-18.0.0.tgz", + "integrity": "sha512-QDHut99rBLHCTAZpq2g4f3VtkpF3znigup3FlEylbWaVha250aaYiQueNMjkLl6yBFEw4SucaDQ1pPBszh/NqQ==", "dev": true, "dependencies": { "tslib": "^2.3.0" @@ -538,30 +614,14 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/core": "17.3.10", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@angular/flex-layout": { - "version": "15.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-15.0.0-beta.42.tgz", - "integrity": "sha512-cTAPVMMxnyIFwpZwdq0PL5mdP9Qh+R8MB7ZBezVaN3Rz2fRrkagzKpLvPX3TFzepXrvHBdpKsU4b8u+NxEC/6g==", - "deprecated": "This package has been deprecated. Please see https://blog.angular.io/modern-css-in-angular-layouts-4a259dca9127", - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/cdk": ">=15.0.0", - "@angular/common": ">=15.0.2", - "@angular/core": ">=15.0.2", - "@angular/platform-browser": ">=15.0.2", + "@angular/core": "18.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/forms": { - "version": "17.3.10", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.3.10.tgz", - "integrity": "sha512-0VZWSXDi2M3DAGJlpdV3lo73Yo/73GPRqmfTOrvIoUIenFg5Dz6oNGzvt/1aRkRn6HKccjix6iMpH91EN65pWA==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.0.0.tgz", + "integrity": "sha512-Q+4WExdgALP7VJ5lKSYmpz8CtAFZI4f3n09JhExIZoPTLD/mqOJcxxO7wTc9lXG4jKSE8BlfgK2txKz1cQvrEQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -569,16 +629,16 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/common": "17.3.10", - "@angular/core": "17.3.10", - "@angular/platform-browser": "17.3.10", + "@angular/common": "18.0.0", + "@angular/core": "18.0.0", + "@angular/platform-browser": "18.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/material": { - "version": "17.3.10", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-17.3.10.tgz", - "integrity": "sha512-hHMQES0tQPH5JW33W+mpBPuM8ybsloDTqFPuRV8cboDjosAWfJhzAKF3ozICpNlUrs62La/2Wu/756GcQrxebg==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.0.0.tgz", + "integrity": "sha512-4WfMcr4cX3cF7dKz+cXf9YIvhWOJGTP24rbMF5C6eC5K20IK6zgA//Qn0VSTwZkm54Tu9C7kF+CfNLeLy6i5uQ==", "dependencies": { "@material/animation": "15.0.0-canary.7f224ddd4.0", "@material/auto-init": "15.0.0-canary.7f224ddd4.0", @@ -623,6 +683,7 @@ "@material/tab-scroller": "15.0.0-canary.7f224ddd4.0", "@material/textfield": "15.0.0-canary.7f224ddd4.0", "@material/theme": "15.0.0-canary.7f224ddd4.0", + "@material/tokens": "15.0.0-canary.7f224ddd4.0", "@material/tooltip": "15.0.0-canary.7f224ddd4.0", "@material/top-app-bar": "15.0.0-canary.7f224ddd4.0", "@material/touch-target": "15.0.0-canary.7f224ddd4.0", @@ -630,19 +691,19 @@ "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/animations": "^17.0.0 || ^18.0.0", - "@angular/cdk": "17.3.10", - "@angular/common": "^17.0.0 || ^18.0.0", - "@angular/core": "^17.0.0 || ^18.0.0", - "@angular/forms": "^17.0.0 || ^18.0.0", - "@angular/platform-browser": "^17.0.0 || ^18.0.0", + "@angular/animations": "^18.0.0 || ^19.0.0", + "@angular/cdk": "18.0.0", + "@angular/common": "^18.0.0 || ^19.0.0", + "@angular/core": "^18.0.0 || ^19.0.0", + "@angular/forms": "^18.0.0 || ^19.0.0", + "@angular/platform-browser": "^18.0.0 || ^19.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/platform-browser": { - "version": "17.3.10", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.3.10.tgz", - "integrity": "sha512-LEhBDOKm2A7nRmZqsafVp6OinRDG1OYZBSqjnT1jZ+f0CRRFIXz6aJ0TMPoU6vq9SLRJ7vrGD9P/eBf2hW00NQ==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.0.0.tgz", + "integrity": "sha512-fOqXQn15H33xGTGgNBUwXAg5KRpqcdsVfipFBuD1GMbjMLQAx/AagxsBavRiq3mKEdHZyQ+hI4mvaKQWOPKUOQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -650,9 +711,9 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/animations": "17.3.10", - "@angular/common": "17.3.10", - "@angular/core": "17.3.10" + "@angular/animations": "18.0.0", + "@angular/common": "18.0.0", + "@angular/core": "18.0.0" }, "peerDependenciesMeta": { "@angular/animations": { @@ -661,9 +722,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "17.3.10", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-17.3.10.tgz", - "integrity": "sha512-TW6G4+isdHM2ssQTRTobeAKtR2516pJ25BSwRb+9+Jw/ZAEYOOi+KQyofIFYQccaUjb3+LpjRcaZbtZ9m/Ispg==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.0.0.tgz", + "integrity": "sha512-Z7Y2qzEuFgCrkgcKPuyHGStEnZ89L3gr3SIgqoVlz4kauf0Fa70H6dxyd/RXV61OZwLXx0yt9rV5d8v+Ay+3fQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -671,16 +732,16 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/common": "17.3.10", - "@angular/compiler": "17.3.10", - "@angular/core": "17.3.10", - "@angular/platform-browser": "17.3.10" + "@angular/common": "18.0.0", + "@angular/compiler": "18.0.0", + "@angular/core": "18.0.0", + "@angular/platform-browser": "18.0.0" } }, "node_modules/@angular/router": { - "version": "17.3.10", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-17.3.10.tgz", - "integrity": "sha512-HlZlR9BOLoEKGOSMjmL5EfYL7F7PeDifbFi0dYWNcrG8zFrVKFklB1cuBdJhfPZgYhDEoGms/EToD71tg5wliA==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.0.0.tgz", + "integrity": "sha512-bytfTypkJbHDv2QkD8jT2w63DWKicSYi5l7N+LPukb9/0pl3XYXKJ8cjlVLbiFvoo5Oz2oBFWYFucWsaPqDw3A==", "dependencies": { "tslib": "^2.3.0" }, @@ -688,19 +749,19 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/common": "17.3.10", - "@angular/core": "17.3.10", - "@angular/platform-browser": "17.3.10", + "@angular/common": "18.0.0", + "@angular/core": "18.0.0", + "@angular/platform-browser": "18.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.6.tgz", + "integrity": "sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.24.7", + "@babel/highlight": "^7.24.6", "picocolors": "^1.0.0" }, "engines": { @@ -708,30 +769,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.6.tgz", + "integrity": "sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", - "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz", + "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.0", - "@babel/parser": "^7.24.0", + "@babel/helper-module-transforms": "^7.24.5", + "@babel/helpers": "^7.24.5", + "@babel/parser": "^7.24.5", "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.0", - "@babel/types": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -762,14 +823,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", + "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", "dev": true, "dependencies": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.24.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { @@ -789,26 +850,25 @@ } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz", - "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.6.tgz", + "integrity": "sha512-+wnfqc5uHiMYtvRX7qu80Toef8BXeh4HHR1SPeonGb1SKPniNEd4a/nlaJJMv/OIEYvIVavvo0yR7u10Gqz0Iw==", "dev": true, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.6.tgz", + "integrity": "sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", + "@babel/compat-data": "^7.24.6", + "@babel/helper-validator-option": "^7.24.6", "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -827,19 +887,19 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.7.tgz", - "integrity": "sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.6.tgz", + "integrity": "sha512-djsosdPJVZE6Vsw3kk7IPRWethP94WHGOhQTc67SNXE0ZzMhHgALw8iGmYS0TD1bbMM0VDROy43od7/hN6WYcA==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-annotate-as-pure": "^7.24.6", + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-function-name": "^7.24.6", + "@babel/helper-member-expression-to-functions": "^7.24.6", + "@babel/helper-optimise-call-expression": "^7.24.6", + "@babel/helper-replace-supers": "^7.24.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.6", + "@babel/helper-split-export-declaration": "^7.24.6", "semver": "^6.3.1" }, "engines": { @@ -850,24 +910,24 @@ } }, "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.6.tgz", + "integrity": "sha512-DitEzDfOMnd13kZnDqns1ccmftwJTS9DMkyn9pYTxulS7bZxUxpMly3Nf23QQ6NwA4UB8lAqjbqWtyvElEMAkg==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz", + "integrity": "sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -883,12 +943,12 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", - "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.6.tgz", + "integrity": "sha512-C875lFBIWWwyv6MHZUG9HmRrlTDgOsLWZfYR0nW69gaKJNe0/Mpxx5r0EID2ZdHQkdUmQo2t0uNckTL08/1BgA==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-annotate-as-pure": "^7.24.6", "regexpu-core": "^5.3.1", "semver": "^6.3.1" }, @@ -900,12 +960,12 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.6.tgz", + "integrity": "sha512-DitEzDfOMnd13kZnDqns1ccmftwJTS9DMkyn9pYTxulS7bZxUxpMly3Nf23QQ6NwA4UB8lAqjbqWtyvElEMAkg==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -937,79 +997,74 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.6.tgz", + "integrity": "sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g==", "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.6.tgz", + "integrity": "sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w==", "dev": true, "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.24.6", + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.6.tgz", + "integrity": "sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz", - "integrity": "sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.6.tgz", + "integrity": "sha512-OTsCufZTxDUsv2/eDXanw/mUZHWOxSbEmC3pP8cgjcy5rgeVPWWMStnv274DV60JtHxTk0adT0QrCzC4M9NWGg==", "dev": true, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.6.tgz", + "integrity": "sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g==", "dev": true, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.6.tgz", + "integrity": "sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-module-imports": "^7.24.6", + "@babel/helper-simple-access": "^7.24.6", + "@babel/helper-split-export-declaration": "^7.24.6", + "@babel/helper-validator-identifier": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1019,47 +1074,47 @@ } }, "node_modules/@babel/helper-module-transforms/node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz", + "integrity": "sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz", - "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.6.tgz", + "integrity": "sha512-3SFDJRbx7KuPRl8XDUr8O7GAEB8iGyWPjLKJh/ywP/Iy9WOmEfMrsWbaZpvBu2HSYn4KQygIsz0O7m8y10ncMA==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.6.tgz", + "integrity": "sha512-MZG/JcWfxybKwsA9N9PmtF2lOSFSEMVCpIRrbxccZFLJPrJciJdG/UhSh5W96GEteJI2ARqm5UAHxISwRDLSNg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", - "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.6.tgz", + "integrity": "sha512-1Qursq9ArRZPAMOZf/nuzVW8HgJLkTB9y9LfP4lW2MVp4e9WkLJDovfKBxoDcCk6VuzIxyqWHyBoaCtSRP10yg==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-wrap-function": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.24.6", + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-wrap-function": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1069,26 +1124,26 @@ } }, "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.6.tgz", + "integrity": "sha512-DitEzDfOMnd13kZnDqns1ccmftwJTS9DMkyn9pYTxulS7bZxUxpMly3Nf23QQ6NwA4UB8lAqjbqWtyvElEMAkg==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", - "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.6.tgz", + "integrity": "sha512-mRhfPwDqDpba8o1F8ESxsEkJMQkUF8ZIWrAc0FtWhxnjfextxMWxr22RtFizxxSYLjVHDeMgVsRq8BBZR2ikJQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7" + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-member-expression-to-functions": "^7.24.6", + "@babel/helper-optimise-call-expression": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1098,105 +1153,102 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.6.tgz", + "integrity": "sha512-nZzcMMD4ZhmB35MOOzQuiGO5RzL6tJbsT37Zx8M5L/i9KSrukGXWTjLe1knIbb/RmxoJE9GON9soq0c0VEMM5g==", "dev": true, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz", - "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.6.tgz", + "integrity": "sha512-jhbbkK3IUKc4T43WadP96a27oYti9gEf1LdyGSP2rHGH77kwLwfhO7TgwnWvxxQVmke0ImmCSS47vcuxEMGD3Q==", "dev": true, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", + "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.6.tgz", + "integrity": "sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz", + "integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.6.tgz", + "integrity": "sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", - "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.6.tgz", + "integrity": "sha512-f1JLrlw/jbiNfxvdrfBgio/gRBk3yTAEJWirpAkiJG2Hb22E7cEYKHWo0dFPTv/niPovzIdPdEDetrv6tC6gPQ==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/helper-function-name": "^7.24.6", + "@babel/template": "^7.24.6", + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.6.tgz", + "integrity": "sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA==", "dev": true, "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.24.6", + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.6.tgz", + "integrity": "sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.6", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" @@ -1206,9 +1258,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.6.tgz", + "integrity": "sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -1218,13 +1270,13 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.7.tgz", - "integrity": "sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.6.tgz", + "integrity": "sha512-bYndrJ6Ph6Ar+GaB5VAc0JPoP80bQCm4qon6JEzXfRl5QZyQ8Ur1K6k7htxWmPA5z+k7JQvaMUrtXlqclWYzKw==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1234,12 +1286,12 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.7.tgz", - "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.6.tgz", + "integrity": "sha512-iVuhb6poq5ikqRq2XWU6OQ+R5o9wF+r/or9CeUyovgptz0UlnK4/seOQ1Istu/XybYjAhQv1FRSSfHHufIku5Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1249,14 +1301,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz", - "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.6.tgz", + "integrity": "sha512-c8TER5xMDYzzFcGqOEp9l4hvB7dcbhcGjcLVwxWfe4P5DOafdwjsBJZKsmv+o3aXh7NhopvayQIovHrh2zSRUQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.6", + "@babel/plugin-transform-optional-chaining": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1266,13 +1318,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.7.tgz", - "integrity": "sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.6.tgz", + "integrity": "sha512-z8zEjYmwBUHN/pCF3NuWBhHQjJCrd33qAi8MgANfMrAvn72k2cImT8VjK9LJFu4ysOLJqhfkYYb3MvwANRUNZQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1357,12 +1409,12 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", - "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.6.tgz", + "integrity": "sha512-BE6o2BogJKJImTmGpkmOic4V0hlRRxVtzqxiSPa8TIFxyhi4EFjHm08nq1M4STK4RytuLMgnSz0/wfflvGFNOg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1372,12 +1424,12 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", - "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.6.tgz", + "integrity": "sha512-D+CfsVZousPXIdudSII7RGy52+dYRtbyKAZcvtQKq/NpsivyMVduepzcLqG5pMBugtMdedxdC8Ramdpcne9ZWQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1529,12 +1581,12 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz", - "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.6.tgz", + "integrity": "sha512-jSSSDt4ZidNMggcLx8SaKsbGNEfIl0PHx/4mFEulorE7bpYLbN0d3pDW3eJ7Y5Z3yPhy3L3NaPCYyTUY7TuugQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1544,13 +1596,13 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz", - "integrity": "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.3.tgz", + "integrity": "sha512-Qe26CMYVjpQxJ8zxM1340JFNjZaF+ISWpr1Kt/jGo+ZTUzKkfw/pphEWbRCb+lmSM6k/TOgfYLvmbHkUQ0asIg==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-remap-async-to-generator": "^7.22.20", "@babel/plugin-syntax-async-generators": "^7.8.4" }, @@ -1562,13 +1614,13 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", - "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.1.tgz", + "integrity": "sha512-AawPptitRXp1y0n4ilKcGbRYWfbbzFWz2NqNu7dacYDtFtz0CMjG64b3LQsb3KIgnf4/obcUL78hfaOS7iCUfw==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-module-imports": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-remap-async-to-generator": "^7.22.20" }, "engines": { @@ -1579,12 +1631,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz", - "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.6.tgz", + "integrity": "sha512-XNW7jolYHW9CwORrZgA/97tL/k05qe/HL0z/qqJq1mdWhwwCM6D4BJBV7wAz9HgFziN5dTOG31znkVIzwxv+vw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1594,12 +1646,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz", - "integrity": "sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.6.tgz", + "integrity": "sha512-S/t1Xh4ehW7sGA7c1j/hiOBLnEYCp/c2sEG4ZkL8kI1xX9tW2pqJTCHKtdhe/jHKt8nG0pFCrDHUXd4DvjHS9w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1609,13 +1661,13 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", - "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.6.tgz", + "integrity": "sha512-j6dZ0Z2Z2slWLR3kt9aOmSIrBvnntWjMDN/TVcMPxhXMLmJVqX605CBRlcGI4b32GMbfifTEsdEjGjiE+j/c3A==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1625,13 +1677,13 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz", - "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.6.tgz", + "integrity": "sha512-1QSRfoPI9RoLRa8Mnakc6v3e0gJxiZQTYrMfLn+mD0sz5+ndSzwymp2hDcYJTyT0MOn0yuWzj8phlIvO72gTHA==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-create-class-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "engines": { @@ -1642,18 +1694,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz", - "integrity": "sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.6.tgz", + "integrity": "sha512-+fN+NO2gh8JtRmDSOB6gaCVo36ha8kfCW1nMq2Gc0DABln0VcHN4PrALDvF5/diLzIRKptC7z/d7Lp64zk92Fg==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-annotate-as-pure": "^7.24.6", + "@babel/helper-compilation-targets": "^7.24.6", + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-function-name": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-replace-supers": "^7.24.6", + "@babel/helper-split-export-declaration": "^7.24.6", "globals": "^11.1.0" }, "engines": { @@ -1664,37 +1716,37 @@ } }, "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.6.tgz", + "integrity": "sha512-DitEzDfOMnd13kZnDqns1ccmftwJTS9DMkyn9pYTxulS7bZxUxpMly3Nf23QQ6NwA4UB8lAqjbqWtyvElEMAkg==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz", + "integrity": "sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", - "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.6.tgz", + "integrity": "sha512-cRzPobcfRP0ZtuIEkA8QzghoUpSB3X3qSH5W2+FzG+VjWbJXExtx0nbRqwumdBN1x/ot2SlTNQLfBCnPdzp6kg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/template": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/template": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1704,12 +1756,12 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz", - "integrity": "sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.6.tgz", + "integrity": "sha512-YLW6AE5LQpk5npNXL7i/O+U9CE4XsBCuRPgyjl1EICZYKmcitV+ayuuUGMJm2lC1WWjXYszeTnIxF/dq/GhIZQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1719,13 +1771,13 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz", - "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.6.tgz", + "integrity": "sha512-rCXPnSEKvkm/EjzOtLoGvKseK+dS4kZwx1HexO3BtRtgL0fQ34awHn34aeSHuXtZY2F8a1X8xqBBPRtOxDVmcA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1735,12 +1787,12 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz", - "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.6.tgz", + "integrity": "sha512-/8Odwp/aVkZwPFJMllSbawhDAO3UJi65foB00HYnK/uXvvCPm0TAXSByjz1mpRmp0q6oX2SIxpkUOpPFHk7FLA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1750,12 +1802,12 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", - "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.6.tgz", + "integrity": "sha512-vpq8SSLRTBLOHUZHSnBqVo0AKX3PBaoPs2vVzYVWslXDTDIpwAcCDtfhUcHSQQoYoUvcFPTdC8TZYXu9ZnLT/w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, "engines": { @@ -1766,13 +1818,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz", - "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.6.tgz", + "integrity": "sha512-EemYpHtmz0lHE7hxxxYEuTYOOBZ43WkDgZ4arQ4r+VX9QHuNZC+WH3wUWmRNvR8ECpTRne29aZV6XO22qpOtdA==", "dev": true, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1782,12 +1834,12 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz", - "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.6.tgz", + "integrity": "sha512-inXaTM1SVrIxCkIJ5gqWiozHfFMStuGbGJAxZFBoHcRRdDP0ySLb3jH6JOwmfiinPwyMZqMBX+7NBDCO4z0NSA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "engines": { @@ -1798,13 +1850,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz", - "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.6.tgz", + "integrity": "sha512-n3Sf72TnqK4nw/jziSqEl1qaWPbCRw2CziHH+jdRYvw4J6yeCzsj4jdw8hIntOEeDGTmHVe2w4MVL44PN0GMzg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1814,14 +1866,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz", - "integrity": "sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.6.tgz", + "integrity": "sha512-sOajCu6V0P1KPljWHKiDq6ymgqB+vfo3isUS4McqW1DZtvSVU2v/wuMhmRmkg3sFoq6GMaUUf8W4WtoSLkOV/Q==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-compilation-targets": "^7.24.6", + "@babel/helper-function-name": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1831,12 +1883,12 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz", - "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.6.tgz", + "integrity": "sha512-Uvgd9p2gUnzYJxVdBLcU0KurF8aVhkmVyMKW4MIY1/BByvs3EBpv45q01o7pRTVmTvtQq5zDlytP3dcUgm7v9w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-json-strings": "^7.8.3" }, "engines": { @@ -1847,12 +1899,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.6.tgz", + "integrity": "sha512-f2wHfR2HF6yMj+y+/y07+SLqnOSwRp8KYLpQKOzS58XLVlULhXbiYcygfXQxJlMbhII9+yXDwOUFLf60/TL5tw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1862,12 +1914,12 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz", - "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.6.tgz", + "integrity": "sha512-EKaWvnezBCMkRIHxMJSIIylzhqK09YpiJtDbr2wsXTwnO0TxyjMUkaw4RlFIZMIS0iDj0KyIg7H7XCguHu/YDA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { @@ -1878,12 +1930,12 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz", - "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.6.tgz", + "integrity": "sha512-9g8iV146szUo5GWgXpRbq/GALTnY+WnNuRTuRHWWFfWGbP9ukRL0aO/jpu9dmOPikclkxnNsjY8/gsWl6bmZJQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1893,13 +1945,13 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz", - "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.6.tgz", + "integrity": "sha512-eAGogjZgcwqAxhyFgqghvoHRr+EYRQPFjUXrTYKBRb5qPnAVxOOglaxc4/byHqjvq/bqO2F3/CGwTHsgKJYHhQ==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-module-transforms": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1909,14 +1961,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz", - "integrity": "sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.6.tgz", + "integrity": "sha512-JEV8l3MHdmmdb7S7Cmx6rbNEjRCgTQMZxllveHO0mx6uiclB0NflCawlQQ6+o5ZrwjUBYPzHm2XoK4wqGVUFuw==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7" + "@babel/helper-module-transforms": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-simple-access": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1926,15 +1978,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", - "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.6.tgz", + "integrity": "sha512-xg1Z0J5JVYxtpX954XqaaAT6NpAY6LtZXvYFCJmGFJWwtlz2EmJoR8LycFRGNE8dBKizGWkGQZGegtkV8y8s+w==", "dev": true, "dependencies": { - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-hoist-variables": "^7.24.6", + "@babel/helper-module-transforms": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-validator-identifier": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1944,13 +1996,13 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz", - "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.6.tgz", + "integrity": "sha512-esRCC/KsSEUvrSjv5rFYnjZI6qv4R1e/iHQrqwbZIoRJqk7xCvEUiN7L1XrmW5QSmQe3n1XD88wbgDTWLbVSyg==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-module-transforms": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1960,13 +2012,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz", - "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.6.tgz", + "integrity": "sha512-6DneiCiu91wm3YiNIGDWZsl6GfTTbspuj/toTEqLh9d4cx50UIzSdg+T96p8DuT7aJOBRhFyaE9ZvTHkXrXr6Q==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1976,12 +2028,12 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz", - "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.6.tgz", + "integrity": "sha512-f8liz9JG2Va8A4J5ZBuaSdwfPqN6axfWRK+y66fjKYbwf9VBLuq4WxtinhJhvp1w6lamKUwLG0slK2RxqFgvHA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1991,12 +2043,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz", - "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.6.tgz", + "integrity": "sha512-+QlAiZBMsBK5NqrBWFXCYeXyiU1y7BQ/OYaiPAcQJMomn5Tyg+r5WuVtyEuvTbpV7L25ZSLfE+2E9ywj4FD48A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "engines": { @@ -2007,12 +2059,12 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz", - "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.6.tgz", + "integrity": "sha512-6voawq8T25Jvvnc4/rXcWZQKKxUNZcKMS8ZNrjxQqoRFernJJKjE3s18Qo6VFaatG5aiX5JV1oPD7DbJhn0a4Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, "engines": { @@ -2023,15 +2075,15 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz", - "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.6.tgz", + "integrity": "sha512-OKmi5wiMoRW5Smttne7BwHM8s/fb5JFs+bVGNSeHWzwZkWXWValR1M30jyXo1s/RaqgwwhEC62u4rFH/FBcBPg==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.7" + "@babel/plugin-transform-parameters": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -2041,13 +2093,13 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz", - "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.6.tgz", + "integrity": "sha512-N/C76ihFKlZgKfdkEYKtaRUtXZAgK7sOY4h2qrbVbVTXPrKGIi8aww5WGe/+Wmg8onn8sr2ut6FXlsbu/j6JHg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-replace-supers": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -2057,12 +2109,12 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz", - "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.6.tgz", + "integrity": "sha512-L5pZ+b3O1mSzJ71HmxSCmTVd03VOT2GXOigug6vDYJzE5awLI7P1g0wFcdmGuwSDSrQ0L2rDOe/hHws8J1rv3w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" }, "engines": { @@ -2073,13 +2125,13 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.7.tgz", - "integrity": "sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.6.tgz", + "integrity": "sha512-cHbqF6l1QP11OkYTYQ+hhVx1E017O5ZcSPXk9oODpqhcAD1htsWG2NpHrrhthEO2qZomLK0FXS+u7NfrkF5aOQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.6", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { @@ -2090,12 +2142,12 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz", - "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.6.tgz", + "integrity": "sha512-ST7guE8vLV+vI70wmAxuZpIKzVjvFX9Qs8bl5w6tN/6gOypPWUmMQL2p7LJz5E63vEGrDhAiYetniJFyBH1RkA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -2105,13 +2157,13 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", - "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.6.tgz", + "integrity": "sha512-T9LtDI0BgwXOzyXrvgLTT8DFjCC/XgWLjflczTLXyvxbnSR/gpv0hbmzlHE/kmh9nOvlygbamLKRo6Op4yB6aw==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -2121,14 +2173,14 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz", - "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.6.tgz", + "integrity": "sha512-Qu/ypFxCY5NkAnEhCF86Mvg3NSabKsh/TPpBVswEdkGl7+FbsYHy1ziRqJpwGH4thBdQHh8zx+z7vMYmcJ7iaQ==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-annotate-as-pure": "^7.24.6", + "@babel/helper-create-class-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { @@ -2139,24 +2191,24 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.6.tgz", + "integrity": "sha512-DitEzDfOMnd13kZnDqns1ccmftwJTS9DMkyn9pYTxulS7bZxUxpMly3Nf23QQ6NwA4UB8lAqjbqWtyvElEMAkg==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", - "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.6.tgz", + "integrity": "sha512-oARaglxhRsN18OYsnPTpb8TcKQWDYNsPNmTnx5++WOAsUJ0cSC/FZVlIJCKvPbU4yn/UXsS0551CFKJhN0CaMw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -2166,12 +2218,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz", - "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.6.tgz", + "integrity": "sha512-SMDxO95I8WXRtXhTAc8t/NFQUT7VYbIWwJCJgEli9ml4MhqUMh4S6hxgH6SmAC3eAQNWCDJFxcFeEt9w2sDdXg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.6", "regenerator-transform": "^0.15.2" }, "engines": { @@ -2182,12 +2234,12 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz", - "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.6.tgz", + "integrity": "sha512-DcrgFXRRlK64dGE0ZFBPD5egM2uM8mgfrvTMOSB2yKzOtjpGegVYkzh3s1zZg1bBck3nkXiaOamJUqK3Syk+4A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -2197,16 +2249,16 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.0.tgz", - "integrity": "sha512-zc0GA5IitLKJrSfXlXmp8KDqLrnGECK7YRfQBmEKg1NmBOQ7e+KuclBEKJgzifQeUYLdNiAw4B4bjyvzWVLiSA==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.3.tgz", + "integrity": "sha512-J0BuRPNlNqlMTRJ72eVptpt9VcInbxO6iP3jaxr+1NPhC0UkKL+6oeX6VXMEYdADnuqmMmsBspt4d5w8Y/TCbQ==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-module-imports": "^7.24.3", "@babel/helper-plugin-utils": "^7.24.0", - "babel-plugin-polyfill-corejs2": "^0.4.8", - "babel-plugin-polyfill-corejs3": "^0.9.0", - "babel-plugin-polyfill-regenerator": "^0.5.5", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.1", + "babel-plugin-polyfill-regenerator": "^0.6.1", "semver": "^6.3.1" }, "engines": { @@ -2226,12 +2278,12 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz", - "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.6.tgz", + "integrity": "sha512-xnEUvHSMr9eOWS5Al2YPfc32ten7CXdH7Zwyyk7IqITg4nX61oHj+GxpNvl+y5JHjfN3KXE2IV55wAWowBYMVw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -2241,13 +2293,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz", - "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.6.tgz", + "integrity": "sha512-h/2j7oIUDjS+ULsIrNZ6/TKG97FgmEk1PXryk/HQq6op4XUUUwif2f69fJrzK0wza2zjCS1xhXmouACaWV5uPA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -2257,12 +2309,12 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz", - "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.6.tgz", + "integrity": "sha512-fN8OcTLfGmYv7FnDrsjodYBo1DhPL3Pze/9mIIE2MGCT1KgADYIOD7rEglpLHZj8PZlC/JFX5WcD+85FLAQusw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -2272,12 +2324,12 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz", - "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.6.tgz", + "integrity": "sha512-BJbEqJIcKwrqUP+KfUIkxz3q8VzXe2R8Wv8TaNgO1cx+nNavxn/2+H8kp9tgFSOL6wYPPEgFvU6IKS4qoGqhmg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -2287,12 +2339,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.7.tgz", - "integrity": "sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.6.tgz", + "integrity": "sha512-IshCXQ+G9JIFJI7bUpxTE/oA2lgVLAIK8q1KdJNoPXOpvRaNjMySGuvLfBw/Xi2/1lLo953uE8hyYSDW3TSYig==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -2302,12 +2354,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", - "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.6.tgz", + "integrity": "sha512-bKl3xxcPbkQQo5eX9LjjDpU2xYHeEeNQbOhj0iPvetSzA+Tu9q/o5lujF4Sek60CM6MgYvOS/DJuwGbiEYAnLw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -2317,13 +2369,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz", - "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.6.tgz", + "integrity": "sha512-8EIgImzVUxy15cZiPii9GvLZwsy7Vxc+8meSlR3cXFmBIl5W5Tn9LGBf7CDKkHj4uVfNXCJB8RsVfnmY61iedA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -2333,13 +2385,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz", - "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.6.tgz", + "integrity": "sha512-pssN6ExsvxaKU638qcWb81RrvvgZom3jDgU/r5xFZ7TONkZGFf4MhI2ltMb8OcQWhHyxgIavEU+hgqtbKOmsPA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -2349,13 +2401,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", - "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.6.tgz", + "integrity": "sha512-quiMsb28oXWIDK0gXLALOJRXLgICLiulqdZGOaPPd0vRT7fQp74NtdADAVu+D8s00C+0Xs0MxVP0VKF/sZEUgw==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -2365,26 +2417,27 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.0.tgz", - "integrity": "sha512-ZxPEzV9IgvGn73iK0E6VB9/95Nd7aMFpbE0l8KQFDG70cOV9IxRP7Y2FUPmlK0v6ImlLqYX50iuZ3ZTVhOF2lA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.5.tgz", + "integrity": "sha512-UGK2ifKtcC8i5AI4cH+sbLLuLc2ktYSFJgBAXorKAsHUZmrQ1q6aQ6i3BvU24wWs2AAKqQB6kq3N9V9Gw1HiMQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.23.5", + "@babel/compat-data": "^7.24.4", "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.5", "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.23.3", - "@babel/plugin-syntax-import-attributes": "^7.23.3", + "@babel/plugin-syntax-import-assertions": "^7.24.1", + "@babel/plugin-syntax-import-attributes": "^7.24.1", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -2396,58 +2449,58 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.23.3", - "@babel/plugin-transform-async-generator-functions": "^7.23.9", - "@babel/plugin-transform-async-to-generator": "^7.23.3", - "@babel/plugin-transform-block-scoped-functions": "^7.23.3", - "@babel/plugin-transform-block-scoping": "^7.23.4", - "@babel/plugin-transform-class-properties": "^7.23.3", - "@babel/plugin-transform-class-static-block": "^7.23.4", - "@babel/plugin-transform-classes": "^7.23.8", - "@babel/plugin-transform-computed-properties": "^7.23.3", - "@babel/plugin-transform-destructuring": "^7.23.3", - "@babel/plugin-transform-dotall-regex": "^7.23.3", - "@babel/plugin-transform-duplicate-keys": "^7.23.3", - "@babel/plugin-transform-dynamic-import": "^7.23.4", - "@babel/plugin-transform-exponentiation-operator": "^7.23.3", - "@babel/plugin-transform-export-namespace-from": "^7.23.4", - "@babel/plugin-transform-for-of": "^7.23.6", - "@babel/plugin-transform-function-name": "^7.23.3", - "@babel/plugin-transform-json-strings": "^7.23.4", - "@babel/plugin-transform-literals": "^7.23.3", - "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", - "@babel/plugin-transform-member-expression-literals": "^7.23.3", - "@babel/plugin-transform-modules-amd": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-modules-systemjs": "^7.23.9", - "@babel/plugin-transform-modules-umd": "^7.23.3", + "@babel/plugin-transform-arrow-functions": "^7.24.1", + "@babel/plugin-transform-async-generator-functions": "^7.24.3", + "@babel/plugin-transform-async-to-generator": "^7.24.1", + "@babel/plugin-transform-block-scoped-functions": "^7.24.1", + "@babel/plugin-transform-block-scoping": "^7.24.5", + "@babel/plugin-transform-class-properties": "^7.24.1", + "@babel/plugin-transform-class-static-block": "^7.24.4", + "@babel/plugin-transform-classes": "^7.24.5", + "@babel/plugin-transform-computed-properties": "^7.24.1", + "@babel/plugin-transform-destructuring": "^7.24.5", + "@babel/plugin-transform-dotall-regex": "^7.24.1", + "@babel/plugin-transform-duplicate-keys": "^7.24.1", + "@babel/plugin-transform-dynamic-import": "^7.24.1", + "@babel/plugin-transform-exponentiation-operator": "^7.24.1", + "@babel/plugin-transform-export-namespace-from": "^7.24.1", + "@babel/plugin-transform-for-of": "^7.24.1", + "@babel/plugin-transform-function-name": "^7.24.1", + "@babel/plugin-transform-json-strings": "^7.24.1", + "@babel/plugin-transform-literals": "^7.24.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.1", + "@babel/plugin-transform-member-expression-literals": "^7.24.1", + "@babel/plugin-transform-modules-amd": "^7.24.1", + "@babel/plugin-transform-modules-commonjs": "^7.24.1", + "@babel/plugin-transform-modules-systemjs": "^7.24.1", + "@babel/plugin-transform-modules-umd": "^7.24.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.23.3", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", - "@babel/plugin-transform-numeric-separator": "^7.23.4", - "@babel/plugin-transform-object-rest-spread": "^7.24.0", - "@babel/plugin-transform-object-super": "^7.23.3", - "@babel/plugin-transform-optional-catch-binding": "^7.23.4", - "@babel/plugin-transform-optional-chaining": "^7.23.4", - "@babel/plugin-transform-parameters": "^7.23.3", - "@babel/plugin-transform-private-methods": "^7.23.3", - "@babel/plugin-transform-private-property-in-object": "^7.23.4", - "@babel/plugin-transform-property-literals": "^7.23.3", - "@babel/plugin-transform-regenerator": "^7.23.3", - "@babel/plugin-transform-reserved-words": "^7.23.3", - "@babel/plugin-transform-shorthand-properties": "^7.23.3", - "@babel/plugin-transform-spread": "^7.23.3", - "@babel/plugin-transform-sticky-regex": "^7.23.3", - "@babel/plugin-transform-template-literals": "^7.23.3", - "@babel/plugin-transform-typeof-symbol": "^7.23.3", - "@babel/plugin-transform-unicode-escapes": "^7.23.3", - "@babel/plugin-transform-unicode-property-regex": "^7.23.3", - "@babel/plugin-transform-unicode-regex": "^7.23.3", - "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", + "@babel/plugin-transform-new-target": "^7.24.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1", + "@babel/plugin-transform-numeric-separator": "^7.24.1", + "@babel/plugin-transform-object-rest-spread": "^7.24.5", + "@babel/plugin-transform-object-super": "^7.24.1", + "@babel/plugin-transform-optional-catch-binding": "^7.24.1", + "@babel/plugin-transform-optional-chaining": "^7.24.5", + "@babel/plugin-transform-parameters": "^7.24.5", + "@babel/plugin-transform-private-methods": "^7.24.1", + "@babel/plugin-transform-private-property-in-object": "^7.24.5", + "@babel/plugin-transform-property-literals": "^7.24.1", + "@babel/plugin-transform-regenerator": "^7.24.1", + "@babel/plugin-transform-reserved-words": "^7.24.1", + "@babel/plugin-transform-shorthand-properties": "^7.24.1", + "@babel/plugin-transform-spread": "^7.24.1", + "@babel/plugin-transform-sticky-regex": "^7.24.1", + "@babel/plugin-transform-template-literals": "^7.24.1", + "@babel/plugin-transform-typeof-symbol": "^7.24.5", + "@babel/plugin-transform-unicode-escapes": "^7.24.1", + "@babel/plugin-transform-unicode-property-regex": "^7.24.1", + "@babel/plugin-transform-unicode-regex": "^7.24.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.1", "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.8", - "babel-plugin-polyfill-corejs3": "^0.9.0", - "babel-plugin-polyfill-regenerator": "^0.5.5", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-regenerator": "^0.6.1", "core-js-compat": "^3.31.0", "semver": "^6.3.1" }, @@ -2488,9 +2541,9 @@ "dev": true }, "node_modules/@babel/runtime": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", - "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" @@ -2500,33 +2553,33 @@ } }, "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.6.tgz", + "integrity": "sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/code-frame": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.6.tgz", + "integrity": "sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/code-frame": "^7.24.6", + "@babel/generator": "^7.24.6", + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-function-name": "^7.24.6", + "@babel/helper-hoist-variables": "^7.24.6", + "@babel/helper-split-export-declaration": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/types": "^7.24.6", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2535,12 +2588,12 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.6.tgz", + "integrity": "sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7", + "@babel/types": "^7.24.6", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -2550,25 +2603,25 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz", + "integrity": "sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.6.tgz", + "integrity": "sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", + "@babel/helper-string-parser": "^7.24.6", + "@babel/helper-validator-identifier": "^7.24.6", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2645,67 +2698,22 @@ "node": ">= 16.0.0" } }, - "node_modules/@compodoc/compodoc/node_modules/@angular-devkit/core": { - "version": "18.0.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.0.1.tgz", - "integrity": "sha512-91eKZoObs+wRgwssw81Y/94Nvixj0WqJkNusBAg+gAfZTCEeJoGGZJkRK8wrONbM79C3Bx8lN/TfSIPRbjnfOQ==", - "dev": true, - "dependencies": { - "ajv": "8.13.0", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.2.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@compodoc/compodoc/node_modules/@angular-devkit/schematics": { - "version": "18.0.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.0.1.tgz", - "integrity": "sha512-AKcEGa3fIgyXT6XTQZWEJZzgmcqlB89fcF7JFOuz4rgQfRmnE2xFw37lKE6ZclCOSiEoffAvgrL8acjdPI1ouw==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "18.0.1", - "jsonc-parser": "3.2.1", - "magic-string": "0.30.10", - "ora": "5.4.1", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, "node_modules/@compodoc/compodoc/node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.6.tgz", + "integrity": "sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/code-frame": "^7.24.6", + "@babel/generator": "^7.24.6", + "@babel/helper-compilation-targets": "^7.24.6", + "@babel/helper-module-transforms": "^7.24.6", + "@babel/helpers": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/template": "^7.24.6", + "@babel/traverse": "^7.24.6", + "@babel/types": "^7.24.6", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -2730,12 +2738,12 @@ } }, "node_modules/@compodoc/compodoc/node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.6.tgz", + "integrity": "sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7", + "@babel/types": "^7.24.6", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -2745,14 +2753,14 @@ } }, "node_modules/@compodoc/compodoc/node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", - "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.6.tgz", + "integrity": "sha512-VEP2o4iR2DqQU6KPgizTW2mnMx6BG5b5O9iQdrW9HesLkv8GIA8x2daXBQxw1MrsIkFQGA/iJ204CKoQ8UcnAA==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-remap-async-to-generator": "^7.24.6", "@babel/plugin-syntax-async-generators": "^7.8.4" }, "engines": { @@ -2763,14 +2771,14 @@ } }, "node_modules/@compodoc/compodoc/node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", - "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.6.tgz", + "integrity": "sha512-NTBA2SioI3OsHeIn6sQmhvXleSl9T70YY/hostQLveWs0ic+qvbA3fa0kwAwQ0OA/XGaAerNZRQGJyRfhbJK4g==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7" + "@babel/helper-module-imports": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-remap-async-to-generator": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -2780,27 +2788,27 @@ } }, "node_modules/@compodoc/compodoc/node_modules/@babel/preset-env": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz", - "integrity": "sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.6.tgz", + "integrity": "sha512-CrxEAvN7VxfjOG8JNF2Y/eMqMJbZPZ185amwGUBp8D9USK90xQmv7dLdFSa+VbD7fdIqcy/Mfv7WtzG8+/qxKg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", + "@babel/compat-data": "^7.24.6", + "@babel/helper-compilation-targets": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-validator-option": "^7.24.6", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.6", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.6", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.7", - "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-assertions": "^7.24.6", + "@babel/plugin-syntax-import-attributes": "^7.24.6", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -2812,54 +2820,54 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.24.7", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.24.7", - "@babel/plugin-transform-class-properties": "^7.24.7", - "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.24.7", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.7", - "@babel/plugin-transform-dotall-regex": "^7.24.7", - "@babel/plugin-transform-duplicate-keys": "^7.24.7", - "@babel/plugin-transform-dynamic-import": "^7.24.7", - "@babel/plugin-transform-exponentiation-operator": "^7.24.7", - "@babel/plugin-transform-export-namespace-from": "^7.24.7", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.24.7", - "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-member-expression-literals": "^7.24.7", - "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-modules-systemjs": "^7.24.7", - "@babel/plugin-transform-modules-umd": "^7.24.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-new-target": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-object-super": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-property-literals": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-reserved-words": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.7", - "@babel/plugin-transform-unicode-escapes": "^7.24.7", - "@babel/plugin-transform-unicode-property-regex": "^7.24.7", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", + "@babel/plugin-transform-arrow-functions": "^7.24.6", + "@babel/plugin-transform-async-generator-functions": "^7.24.6", + "@babel/plugin-transform-async-to-generator": "^7.24.6", + "@babel/plugin-transform-block-scoped-functions": "^7.24.6", + "@babel/plugin-transform-block-scoping": "^7.24.6", + "@babel/plugin-transform-class-properties": "^7.24.6", + "@babel/plugin-transform-class-static-block": "^7.24.6", + "@babel/plugin-transform-classes": "^7.24.6", + "@babel/plugin-transform-computed-properties": "^7.24.6", + "@babel/plugin-transform-destructuring": "^7.24.6", + "@babel/plugin-transform-dotall-regex": "^7.24.6", + "@babel/plugin-transform-duplicate-keys": "^7.24.6", + "@babel/plugin-transform-dynamic-import": "^7.24.6", + "@babel/plugin-transform-exponentiation-operator": "^7.24.6", + "@babel/plugin-transform-export-namespace-from": "^7.24.6", + "@babel/plugin-transform-for-of": "^7.24.6", + "@babel/plugin-transform-function-name": "^7.24.6", + "@babel/plugin-transform-json-strings": "^7.24.6", + "@babel/plugin-transform-literals": "^7.24.6", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.6", + "@babel/plugin-transform-member-expression-literals": "^7.24.6", + "@babel/plugin-transform-modules-amd": "^7.24.6", + "@babel/plugin-transform-modules-commonjs": "^7.24.6", + "@babel/plugin-transform-modules-systemjs": "^7.24.6", + "@babel/plugin-transform-modules-umd": "^7.24.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.6", + "@babel/plugin-transform-new-target": "^7.24.6", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.6", + "@babel/plugin-transform-numeric-separator": "^7.24.6", + "@babel/plugin-transform-object-rest-spread": "^7.24.6", + "@babel/plugin-transform-object-super": "^7.24.6", + "@babel/plugin-transform-optional-catch-binding": "^7.24.6", + "@babel/plugin-transform-optional-chaining": "^7.24.6", + "@babel/plugin-transform-parameters": "^7.24.6", + "@babel/plugin-transform-private-methods": "^7.24.6", + "@babel/plugin-transform-private-property-in-object": "^7.24.6", + "@babel/plugin-transform-property-literals": "^7.24.6", + "@babel/plugin-transform-regenerator": "^7.24.6", + "@babel/plugin-transform-reserved-words": "^7.24.6", + "@babel/plugin-transform-shorthand-properties": "^7.24.6", + "@babel/plugin-transform-spread": "^7.24.6", + "@babel/plugin-transform-sticky-regex": "^7.24.6", + "@babel/plugin-transform-template-literals": "^7.24.6", + "@babel/plugin-transform-typeof-symbol": "^7.24.6", + "@babel/plugin-transform-unicode-escapes": "^7.24.6", + "@babel/plugin-transform-unicode-property-regex": "^7.24.6", + "@babel/plugin-transform-unicode-regex": "^7.24.6", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.6", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.4", @@ -2883,39 +2891,6 @@ "semver": "bin/semver.js" } }, - "node_modules/@compodoc/compodoc/node_modules/ajv": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", - "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@compodoc/compodoc/node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "dev": true, - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, "node_modules/@compodoc/compodoc/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -2931,31 +2906,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@compodoc/compodoc/node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", - "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.1", - "core-js-compat": "^3.36.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@compodoc/compodoc/node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, "node_modules/@compodoc/compodoc/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3005,48 +2955,6 @@ "node": ">=8" } }, - "node_modules/@compodoc/compodoc/node_modules/magic-string": { - "version": "0.30.10", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", - "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - } - }, - "node_modules/@compodoc/compodoc/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@compodoc/compodoc/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@compodoc/compodoc/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@compodoc/compodoc/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -3163,10 +3071,266 @@ "node": ">=10.0.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.3.tgz", + "integrity": "sha512-yTgnwQpFVYfvvo4SvRFB0SwrW8YjOxEoT7wfMT7Ol5v7v5LDNvSGo67aExmxOb87nQNeWPVvaGBNfQ7BXcrZ9w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.3.tgz", + "integrity": "sha512-bviJOLMgurLJtF1/mAoJLxDZDL6oU5/ztMHnJQRejbJrSc9FFu0QoUoFhvi6qSKJEw9y5oGyvr9fuDtzJ30rNQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.3.tgz", + "integrity": "sha512-c+ty9necz3zB1Y+d/N+mC6KVVkGUUOcm4ZmT5i/Fk5arOaY3i6CA3P5wo/7+XzV8cb4GrI/Zjp8NuOQ9Lfsosw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.3.tgz", + "integrity": "sha512-JReHfYCRK3FVX4Ra+y5EBH1b9e16TV2OxrPAvzMsGeES0X2Ndm9ImQRI4Ket757vhc5XBOuGperw63upesclRw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.3.tgz", + "integrity": "sha512-U3fuQ0xNiAkXOmQ6w5dKpEvXQRSpHOnbw7gEfHCRXPeTKW9sBzVck6C5Yneb8LfJm0l6le4NQfkNPnWMSlTFUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.3.tgz", + "integrity": "sha512-3m1CEB7F07s19wmaMNI2KANLcnaqryJxO1fXHUV5j1rWn+wMxdUYoPyO2TnAbfRZdi7ADRwJClmOwgT13qlP3Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.3.tgz", + "integrity": "sha512-fsNAAl5pU6wmKHq91cHWQT0Fz0vtyE1JauMzKotrwqIKAswwP5cpHUCxZNSTuA/JlqtScq20/5KZ+TxQdovU/g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.3.tgz", + "integrity": "sha512-tci+UJ4zP5EGF4rp8XlZIdq1q1a/1h9XuronfxTMCNBslpCtmk97Q/5qqy1Mu4zIc0yswN/yP/BLX+NTUC1bXA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.3.tgz", + "integrity": "sha512-f6kz2QpSuyHHg01cDawj0vkyMwuIvN62UAguQfnNVzbge2uWLhA7TCXOn83DT0ZvyJmBI943MItgTovUob36SQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.3.tgz", + "integrity": "sha512-vvG6R5g5ieB4eCJBQevyDMb31LMHthLpXTc2IGkFnPWS/GzIFDnaYFp558O+XybTmYrVjxnryru7QRleJvmZ6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.3.tgz", + "integrity": "sha512-HjCWhH7K96Na+66TacDLJmOI9R8iDWDDiqe17C7znGvvE4sW1ECt9ly0AJ3dJH62jHyVqW9xpxZEU1jKdt+29A==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.3.tgz", + "integrity": "sha512-BGpimEccmHBZRcAhdlRIxMp7x9PyJxUtj7apL2IuoG9VxvU/l/v1z015nFs7Si7tXUwEsvjc1rOJdZCn4QTU+Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.3.tgz", + "integrity": "sha512-5rMOWkp7FQGtAH3QJddP4w3s47iT20hwftqdm7b+loe95o8JU8ro3qZbhgMRy0VuFU0DizymF1pBKkn3YHWtsw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.3.tgz", + "integrity": "sha512-h0zj1ldel89V5sjPLo5H1SyMzp4VrgN1tPkN29TmjvO1/r0MuMRwJxL8QY05SmfsZRs6TF0c/IDH3u7XYYmbAg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.3.tgz", + "integrity": "sha512-dkAKcTsTJ+CRX6bnO17qDJbLoW37npd5gSNtSzjYQr0svghLJYGYB0NF1SNcU1vDcjXLYS5pO4qOW4YbFama4A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.3.tgz", + "integrity": "sha512-vnD1YUkovEdnZWEuMmy2X2JmzsHQqPpZElXx6dxENcIwTu+Cu5ERax6+Ke1QsE814Zf3c6rxCfwQdCTQ7tPuXA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/linux-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.1.tgz", - "integrity": "sha512-5gRPk7pKuaIB+tmH+yKd2aQTRpqlf1E4f/mC+tawIm/CGJemZcHZpp2ic8oD83nKgUPMEd0fNanrnFljiruuyA==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.3.tgz", + "integrity": "sha512-IOXOIm9WaK7plL2gMhsWJd+l2bfrhfilv0uPTptoRoSb2p09RghhQQp9YY6ZJhk/kqmeRt6siRdMSLLwzuT0KQ==", "cpu": [ "x64" ], @@ -3179,6 +3343,257 @@ "node": ">=12" } }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.3.tgz", + "integrity": "sha512-uTgCwsvQ5+vCQnqM//EfDSuomo2LhdWhFPS8VL8xKf+PKTCrcT/2kPPoWMTs22aB63MLdGMJiE3f1PHvCDmUOw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.3.tgz", + "integrity": "sha512-vNAkR17Ub2MgEud2Wag/OE4HTSI6zlb291UYzHez/psiKarp0J8PKGDnAhMBcHFoOHMXHfExzmjMojJNbAStrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.3.tgz", + "integrity": "sha512-W8H9jlGiSBomkgmouaRoTXo49j4w4Kfbl6I1bIdO/vT0+0u4f20ko3ELzV3hPI6XV6JNBVX+8BC+ajHkvffIJA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.3.tgz", + "integrity": "sha512-EjEomwyLSCg8Ag3LDILIqYCZAq/y3diJ04PnqGRgq8/4O3VNlXyMd54j/saShaN4h5o5mivOjAzmU6C3X4v0xw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.3.tgz", + "integrity": "sha512-WGiE/GgbsEwR33++5rzjiYsKyHywE8QSZPF7Rfx9EBfK3Qn3xyR6IjyCr5Uk38Kg8fG4/2phN7sXp4NPWd3fcw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.3.tgz", + "integrity": "sha512-xRxC0jaJWDLYvcUvjQmHCJSfMrgmUuvsoXgDeU/wTorQ1ngDdUBuFtgY3W1Pc5sprGAvZBtWdJX7RPg/iZZUqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "peer": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.15.1.tgz", + "integrity": "sha512-K4gzNq+yymn/EVsXYmf+SBcBro8MTf+aXJZUphM96CdzUEr+ClGDvAbpmaEK+cGVigVXIgs9gNmvHAlrzzY5JQ==", + "dev": true, + "peer": true, + "dependencies": { + "@eslint/object-schema": "^2.1.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "peer": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "peer": true + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "peer": true + }, + "node_modules/@eslint/js": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.4.0.tgz", + "integrity": "sha512-fdI7VJjP3Rvc70lC4xkFXHB0fiPeojiL1PxVG6t1ZvXQrarj893PweuBTujxDUFk0Fxj4R7PIIAZ/aiiyZPZcg==", + "dev": true, + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.3.tgz", + "integrity": "sha512-HAbhAYKfsAC2EkTqve00ibWIZlaU74Z1EHwAjYr4PXF0YU2VEA1zSIKSSpKszRLRWwHzzRZXvK632u+uXzvsvw==", + "dev": true, + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@foliojs-fork/fontkit": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/@foliojs-fork/fontkit/-/fontkit-1.9.2.tgz", @@ -3229,6 +3644,43 @@ "integrity": "sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA==", "dev": true }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, + "peer": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.3.tgz", + "integrity": "sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -3408,20 +3860,66 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.0.4.tgz", + "integrity": "sha512-aOcSN4MeAtFROysrbqG137b7gaDDSmVrl5mpo6sT/w+kcXpWnzhMjmY/Fh/sDx26NBxyIE7MB1seqLeCAzy9Sg==", + "dev": true, + "dependencies": { + "@jsonjoy.com/base64": "^1.1.1", + "@jsonjoy.com/util": "^1.1.2", + "hyperdyperid": "^1.2.0", + "thingies": "^1.20.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.1.3.tgz", + "integrity": "sha512-g//kkF4kOwUjemValCtOc/xiYzmwMRmWq3Bn+YnzOzuZLHq2PpMOxxIayN3cKbo7Ko2Np65t6D9H81IvXbXhqg==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", "dev": true }, - "node_modules/@livekit/egress-sdk": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@livekit/egress-sdk/-/egress-sdk-0.2.0.tgz", - "integrity": "sha512-UVWAIEf8PSWytBorfOw4nbVl/0HoOuVOPr/XS5kQhcrM4TaeG6Sl0KXIKQGvU4xbaBW0WStyvGOBFSzM94OD7g==", - "peerDependencies": { - "livekit-client": "^1.15.13 || ^2.0.10" - } - }, "node_modules/@livekit/protocol": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/@livekit/protocol/-/protocol-1.13.0.tgz", @@ -3431,22 +3929,17 @@ } }, "node_modules/@livekit/track-processors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@livekit/track-processors/-/track-processors-0.3.2.tgz", - "integrity": "sha512-4JUCzb7yIKoVsTo8J6FTzLZJHcI6DihfX/pGRDg0SOGaxprcDPrt8jaDBBTsnGBSXHeMxl2ugN+xQjdCWzLKEA==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@livekit/track-processors/-/track-processors-0.3.1.tgz", + "integrity": "sha512-lHOsqP7eiuwyK3eNVkkOw+sUFfj2v8hYxbRbLd1c4lJYykLOyfm90VhfQdXoJ7LtlptSUleB6HOl1sfw09cEpA==", "dependencies": { "@mediapipe/holistic": "0.5.1675471629", "@mediapipe/tasks-vision": "0.10.9" }, "peerDependencies": { - "livekit-client": "^1.12.0 || ^2.1.0" + "livekit-client": "^1.12.0 || ^2.0.0" } }, - "node_modules/@livekit/track-processors/node_modules/@mediapipe/tasks-vision": { - "version": "0.10.9", - "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.9.tgz", - "integrity": "sha512-/gFguyJm1ng4Qr7VVH2vKO+zZcQd8wc3YafUfvBuYFX0Y5+CvrV+VNPEVkl5W/gUZF5KNKNZAiaHPULGPCIjyQ==" - }, "node_modules/@ljharb/through": { "version": "2.3.13", "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", @@ -3459,6 +3952,84 @@ "node": ">= 0.4" } }, + "node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.0.8.tgz", + "integrity": "sha512-+lFwFvU+zQ9zVIFETNtmW++syh3Ps5JS8MPQ8zOYtQZoU+dTR8ivWHTaE2QVk1JG2payGDLUAvpndLAjGMdeeA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-darwin-x64": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.0.8.tgz", + "integrity": "sha512-T98rfsgfdQMS5/mqdsPb6oHSJ+iBYNa+PQDLtXLh6rzTEBsYP9x2uXxIj6VS4qXVDWXVi8rv85NCOG+UBOsHXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.0.8.tgz", + "integrity": "sha512-gVNCi3bYWatdPMeFpFjuZl6bzVL55FkeZU3sPeU+NsMRXC+Zl3qOx3M6cM4OMlJWbhHjYjf2b8q83K0mczaiWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm64": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.0.8.tgz", + "integrity": "sha512-uEBGCQIChsixpykL0pjCxfF64btv64vzsb1NoM5u0qvabKvKEvErhXGoqovyldDu9u1T/fswD8Kf6ih0vJEvDQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-x64": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.0.8.tgz", + "integrity": "sha512-6v0B4sa9ulNezmDZtVpLjNHmA0qZzUl3001YJ2RF0naxsuv/Jq/xEwNYpOzfcdizHfpCE0oBkWzk/r+Slr+0zw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-win32-x64": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.0.8.tgz", + "integrity": "sha512-lDLGRIMqdwYD39vinwNqqZUxCdL2m2iIdn+0HyQgIHEiT0g5rIAlzaMKzoGWon5NQumfxXFk9y0DarttkR7C1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@material/animation": { "version": "15.0.0-canary.7f224ddd4.0", "resolved": "https://registry.npmjs.org/@material/animation/-/animation-15.0.0-canary.7f224ddd4.0.tgz", @@ -4217,23 +4788,101 @@ "integrity": "sha512-qY+cxtDeSOvVtevrLgnodiwXYaAtPi7dHZtNv/bUCGEjFicAOYtMmrZSqMmbPkTB2+4jLnPF1vgshkAqQRSYAw==" }, "node_modules/@mediapipe/tasks-vision": { - "version": "0.10.12", - "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.12.tgz", - "integrity": "sha512-688Vukid7hvGmx+7hzS/EQ3Q4diz4eeX4/FYDw8f/t56UjFueD8LTvA2rX5BCIwvT0oy8QHKh5uKIyct1AOFtQ==" + "version": "0.10.9", + "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.9.tgz", + "integrity": "sha512-/gFguyJm1ng4Qr7VVH2vKO+zZcQd8wc3YafUfvBuYFX0Y5+CvrV+VNPEVkl5W/gUZF5KNKNZAiaHPULGPCIjyQ==" + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz", + "integrity": "sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.2.tgz", + "integrity": "sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.2.tgz", + "integrity": "sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.2.tgz", + "integrity": "sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.2.tgz", + "integrity": "sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.2.tgz", + "integrity": "sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] }, "node_modules/@ngtools/webpack": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.3.8.tgz", - "integrity": "sha512-CjSVVa/9fzMpEDQP01SC4colKCbZwj7vUq0H2bivp8jVsmd21x9Fu0gDBH0Y9NdfAIm4eGZvmiZKMII3vIOaYQ==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.0.1.tgz", + "integrity": "sha512-uetWaviDUK3lgjKxN/FOxhEuZ5O3PVY8vWFAv1LkPSLFJbcKAQZlYbKnrn7uvQzyrkUc3W5+bYEGx2OcXMpb9g==", "dev": true, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "peerDependencies": { - "@angular/compiler-cli": "^17.0.0", - "typescript": ">=5.2 <5.5", + "@angular/compiler-cli": "^18.0.0", + "typescript": ">=5.4 <5.5", "webpack": "^5.54.0" } }, @@ -4346,15 +4995,6 @@ "node": "14 || >=16.14" } }, - "node_modules/@npmcli/git/node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/@npmcli/git/node_modules/which": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", @@ -4396,9 +5036,9 @@ } }, "node_modules/@npmcli/package-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.2.0.tgz", - "integrity": "sha512-qe/kiqqkW0AGtvBjL8TJKZk/eBBSpnJkUWvHdQ9jM2lKHXRYYJuyNpJPlJw3c8QjC2ow6NZYiLExhUaeJelbxQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.1.1.tgz", + "integrity": "sha512-uTq5j/UqUzbOaOxVy+osfOhpqOiLfUZ0Ut33UbcyyAPJbZcJsf4Mrsyb8r58FoIFlofw0iOFsuCA/oDK14VDJQ==", "dev": true, "dependencies": { "@npmcli/git": "^5.0.0", @@ -4413,15 +5053,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@npmcli/package-json/node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/@npmcli/promise-spawn": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", @@ -4459,24 +5090,25 @@ } }, "node_modules/@npmcli/redact": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-1.1.0.tgz", - "integrity": "sha512-PfnWuOkQgu7gCbnSsAisaX7hKOdZ4wSAhAzH3/ph5dSGau52kCRrMMGbiSQLwyTZpgldkZ49b0brkOr1AzGBHQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.0.tgz", + "integrity": "sha512-SEjCPAVHWYUIQR+Yn03kJmrJjZDtJLYpj300m3HV9OTRZNpC5YpbMsM3eTkECyT4aWj8lDr9WeY6TWefpubtYQ==", "dev": true, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/@npmcli/run-script": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-7.0.4.tgz", - "integrity": "sha512-9ApYM/3+rBt9V80aYg6tZfzj3UWdiYyCt7gJUD1VJKvWF5nwKDSICXbYIQbspFTq6TOpbsEtIC0LArB8d9PFmg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-8.1.0.tgz", + "integrity": "sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==", "dev": true, "dependencies": { "@npmcli/node-gyp": "^3.0.0", "@npmcli/package-json": "^5.0.0", "@npmcli/promise-spawn": "^7.0.0", "node-gyp": "^10.0.0", + "proc-log": "^4.0.0", "which": "^4.0.0" }, "engines": { @@ -4517,6 +5149,18 @@ "node": ">=14" } }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.25", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", @@ -4602,6 +5246,149 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", + "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", + "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", + "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", + "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", + "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", + "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", + "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", + "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", + "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", + "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", + "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", @@ -4615,6 +5402,58 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", + "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", + "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", + "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", + "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/wasm-node": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/wasm-node/-/wasm-node-4.18.0.tgz", @@ -4635,17 +5474,17 @@ } }, "node_modules/@schematics/angular": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-17.3.8.tgz", - "integrity": "sha512-2g4OmSyE9YGq50Uj7fNI26P/TSAFJ7ZuirwTF2O7Xc4XRQ29/tYIIqhezpNlTb6rlYblcQuMcUZBrMfWJHcqJw==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.0.1.tgz", + "integrity": "sha512-ho9QOUiS4wqKRzbKFWUGU8iecfcdrjnrjBXbzJEQ6GNIOz7iDniLMNXYRP7P+xanWQGLPDIOVR2lGaryPdTXDw==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.3.8", - "@angular-devkit/schematics": "17.3.8", + "@angular-devkit/core": "18.0.1", + "@angular-devkit/schematics": "18.0.1", "jsonc-parser": "3.2.1" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } @@ -4697,15 +5536,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@sigstore/sign/node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/@sigstore/tuf": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.4.tgz", @@ -4736,7 +5566,8 @@ "node_modules/@socket.io/component-emitter": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true }, "node_modules/@testim/chrome-version": { "version": "1.1.4", @@ -4764,12 +5595,6 @@ "pnpm": ">=8.6.0" } }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "dev": true - }, "node_modules/@ts-morph/common": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.23.0.tgz", @@ -4782,6 +5607,30 @@ "path-browserify": "^1.0.1" } }, + "node_modules/@ts-morph/common/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@ts-morph/common/node_modules/mkdirp": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", @@ -4843,6 +5692,30 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -4863,9 +5736,9 @@ } }, "node_modules/@types/chai": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", - "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw==", "dev": true }, "node_modules/@types/connect": { @@ -4902,6 +5775,21 @@ "@types/node": "*" } }, + "node_modules/@types/dom-mediacapture-transform": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@types/dom-mediacapture-transform/-/dom-mediacapture-transform-0.1.9.tgz", + "integrity": "sha512-/K96dASG23bqF+VAftybbI5SUj9qSsdsSKZglm7Bq/sIaEve5z8I+GdClARcSQMAAVkH7bc83UI1jiH/qc5LMw==", + "dev": true, + "dependencies": { + "@types/dom-webcodecs": "*" + } + }, + "node_modules/@types/dom-webcodecs": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.11.tgz", + "integrity": "sha512-yPEZ3z7EohrmOxbk/QTAa0yonMFkNkjnVXqbGb7D4rMr+F1dGQ8ZUFxXkyLLJuiICPejZ0AZE9Rrk9wUCczx4A==", + "dev": true + }, "node_modules/@types/eslint": { "version": "8.56.10", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", @@ -4941,9 +5829,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.5", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", - "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", + "version": "4.19.3", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.3.tgz", + "integrity": "sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg==", "dev": true, "dependencies": { "@types/node": "*", @@ -4986,9 +5874,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.12.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", - "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -5022,15 +5910,15 @@ "dev": true }, "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", "dev": true }, "node_modules/@types/selenium-webdriver": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-4.1.5.tgz", - "integrity": "sha512-Lfu97JK5b2jAxCUHH8uMjmhUiQZCGDyVzSAskFFZuWcprtcwjMkEPZE/SiIM5hOGQJVs982BAF26a3kmw8iiJw==", + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-4.1.16.tgz", + "integrity": "sha512-ETje9rr7nTrD0r/mNnIuCAF7fAZ2xKE/1WyxXZZH9N9Cy2NKJTrpEd7SCdzuIlm/1iu1gjHCVbaDwT+MuDrVZg==", "dev": true, "dependencies": { "@types/ws": "*" @@ -5076,9 +5964,9 @@ } }, "node_modules/@types/ws": { - "version": "8.5.4", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", - "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "dev": true, "dependencies": { "@types/node": "*" @@ -5299,9 +6187,9 @@ } }, "node_modules/acorn": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", - "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -5319,14 +6207,21 @@ "acorn": "^8" } }, - "node_modules/acorn-walk": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", - "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", "dev": true, - "dependencies": { - "acorn": "^8.11.0" - }, "engines": { "node": ">=0.4.0" } @@ -5384,15 +6279,15 @@ } }, "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" }, "funding": { "type": "github", @@ -5400,9 +6295,9 @@ } }, "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "dev": true, "dependencies": { "ajv": "^8.0.0" @@ -5630,18 +6525,6 @@ "node": "*" } }, - "node_modules/ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "dev": true, - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", @@ -5672,9 +6555,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.18", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz", - "integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", + "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", "dev": true, "funding": [ { @@ -5692,7 +6575,7 @@ ], "dependencies": { "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001591", + "caniuse-lite": "^1.0.30001599", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -5800,57 +6683,25 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz", - "integrity": "sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg==", + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", + "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.5.0", - "core-js-compat": "^3.34.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3/node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", - "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", - "dev": true, - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" + "@babel/helper-define-polyfill-provider": "^0.6.1", + "core-js-compat": "^3.36.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz", - "integrity": "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", + "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.5.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator/node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", - "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", - "dev": true, - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" + "@babel/helper-define-polyfill-provider": "^0.6.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -5903,15 +6754,6 @@ "node": ">= 0.8" } }, - "node_modules/basic-ftp": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", - "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -6038,12 +6880,13 @@ } }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/braces": { @@ -6074,9 +6917,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.23.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", - "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "dev": true, "funding": [ { @@ -6093,10 +6936,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001629", - "electron-to-chromium": "^1.4.796", + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.16" + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -6156,6 +6999,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -6244,9 +7102,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001636", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz", - "integrity": "sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==", + "version": "1.0.30001627", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001627.tgz", + "integrity": "sha512-4zgNiB8nTyV/tHhwZrFs88ryjls/lHiqFhrxCW4qSTeuRByBVnPYpDInchOIySWknznucaf31Z4KYqjfbrecVw==", "dev": true, "funding": [ { @@ -6394,25 +7252,50 @@ } }, "node_modules/chromedriver": { - "version": "122.0.5", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-122.0.5.tgz", - "integrity": "sha512-5WkCY4ioJZ1Qna6KWqPSrIz1MwGh6tHW7F67XTSbZn/GaMJrpiuy6b5c1BetrddSax+NX8u4tg4l3Wy1THecLQ==", + "version": "116.0.0", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-116.0.0.tgz", + "integrity": "sha512-/TQaRn+RUAYnVqy5Vx8VtU8DvtWosU8QLM2u7BoNM5h55PRQPXF/onHAehEi8Sj/CehdKqH50NFdiumQAUr0DQ==", "dev": true, "hasInstallScript": true, "dependencies": { - "@testim/chrome-version": "^1.1.4", - "axios": "^1.6.7", - "compare-versions": "^6.1.0", + "@testim/chrome-version": "^1.1.3", + "axios": "^1.4.0", + "compare-versions": "^6.0.0", "extract-zip": "^2.0.1", - "proxy-agent": "^6.4.0", + "https-proxy-agent": "^5.0.1", "proxy-from-env": "^1.1.0", - "tcp-port-used": "^1.0.2" + "tcp-port-used": "^1.0.1" }, "bin": { "chromedriver": "bin/chromedriver" }, "engines": { - "node": ">=18" + "node": ">=16" + } + }, + "node_modules/chromedriver/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/chromedriver/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" } }, "node_modules/clean-stack": { @@ -6448,6 +7331,72 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/cli-width": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", @@ -7153,22 +8102,22 @@ "dev": true }, "node_modules/css-loader": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.10.0.tgz", - "integrity": "sha512-LTSA/jWbwdMlk+rhmElbDR2vbtQoTBPr7fkJE+mxrHj+7ru0hUmHafDRzWIjIHTwpitWVaqY2/UWGRca3yUgRw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.1.tgz", + "integrity": "sha512-OxIR5P2mjO1PSXk44bWuQ8XtMK4dpEqpIyERCx3ewOo3I8EmbcxMPUc5ScLtQfgXtOojoMv57So4V/C02HQLsw==", "dev": true, "dependencies": { "icss-utils": "^5.1.0", "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.4", - "postcss-modules-scope": "^3.1.1", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", "semver": "^7.5.4" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", @@ -7176,7 +8125,7 @@ }, "peerDependencies": { "@rspack/core": "0.x || 1.x", - "webpack": "^5.0.0" + "webpack": "^5.27.0" }, "peerDependenciesMeta": { "@rspack/core": { @@ -7258,15 +8207,6 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, - "node_modules/data-uri-to-buffer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", - "dev": true, - "engines": { - "node": ">= 14" - } - }, "node_modules/data-view-buffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", @@ -7337,6 +8277,7 @@ "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -7417,6 +8358,34 @@ "node": ">=0.10.0" } }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/default-gateway": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", @@ -7429,6 +8398,113 @@ "node": ">= 10" } }, + "node_modules/default-gateway/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/default-gateway/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-gateway/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/default-gateway/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-gateway/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/default-gateway/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/default-gateway/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-gateway/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/default-gateway/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/defaults": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", @@ -7484,20 +8560,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", - "dev": true, - "dependencies": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -7535,6 +8597,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/detect-node": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", @@ -7678,9 +8749,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.807", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.807.tgz", - "integrity": "sha512-kSmJl2ZwhNf/bcIuCH/imtNOKlpkLDn2jqT5FJ+/0CXjhnFaOa9cOe9gHKKy71eM49izwuQjZhKk+lWQ1JxB7A==", + "version": "1.4.788", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.788.tgz", + "integrity": "sha512-ubp5+Ev/VV8KuRoWnfP2QF2Bg+O2ZFdb49DiiNbz2VmgkIqrnyYaqIOqj8A6K/3p1xV0QcU5hBQ1+BmB6ot1OA==", "dev": true }, "node_modules/emitter-component": { @@ -7749,9 +8820,9 @@ } }, "node_modules/engine.io": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", - "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", + "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", "dev": true, "dependencies": { "@types/cookie": "^0.4.1", @@ -7763,36 +8834,46 @@ "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", - "ws": "~8.17.1" + "ws": "~8.11.0" }, "engines": { "node": ">=10.2.0" } }, - "node_modules/engine.io-client": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz", - "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1", - "xmlhttprequest-ssl": "~2.0.0" - } - }, "node_modules/engine.io-parser": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "dev": true, "engines": { "node": ">=10.0.0" } }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/enhanced-resolve": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", - "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", + "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -7803,16 +8884,10 @@ } }, "node_modules/ent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.1.tgz", - "integrity": "sha512-QHuXVeZx9d+tIQAz/XztU0ZwZf2Agg9CcXcgE1rurqvdBeDBrpSwjl8/6XUqMg7tw2Y7uAdKb2sRv+bSEFqQ5A==", - "dev": true, - "dependencies": { - "punycode": "^1.4.1" - }, - "engines": { - "node": ">= 0.4" - } + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", + "dev": true }, "node_modules/entities": { "version": "4.5.0", @@ -8000,12 +9075,11 @@ "dev": true }, "node_modules/esbuild": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.1.tgz", - "integrity": "sha512-OJwEgrpWm/PCMsLVWXKqvcjme3bHNpOgN7Tb6cQnR5n0TPbQx1/Xrn7rqM+wn17bYeT6MGB5sn1Bh5YiGi70nA==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.3.tgz", + "integrity": "sha512-Kgq0/ZsAPzKrbOjCQcjoSmPoWhlcVnGAUo7jvaLHoxW1Drto0KGkR1xBNg2Cp43b9ImvxmPEJZ9xkfcnqPsfBw==", "dev": true, "hasInstallScript": true, - "optional": true, "bin": { "esbuild": "bin/esbuild" }, @@ -8013,35 +9087,35 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.1", - "@esbuild/android-arm": "0.20.1", - "@esbuild/android-arm64": "0.20.1", - "@esbuild/android-x64": "0.20.1", - "@esbuild/darwin-arm64": "0.20.1", - "@esbuild/darwin-x64": "0.20.1", - "@esbuild/freebsd-arm64": "0.20.1", - "@esbuild/freebsd-x64": "0.20.1", - "@esbuild/linux-arm": "0.20.1", - "@esbuild/linux-arm64": "0.20.1", - "@esbuild/linux-ia32": "0.20.1", - "@esbuild/linux-loong64": "0.20.1", - "@esbuild/linux-mips64el": "0.20.1", - "@esbuild/linux-ppc64": "0.20.1", - "@esbuild/linux-riscv64": "0.20.1", - "@esbuild/linux-s390x": "0.20.1", - "@esbuild/linux-x64": "0.20.1", - "@esbuild/netbsd-x64": "0.20.1", - "@esbuild/openbsd-x64": "0.20.1", - "@esbuild/sunos-x64": "0.20.1", - "@esbuild/win32-arm64": "0.20.1", - "@esbuild/win32-ia32": "0.20.1", - "@esbuild/win32-x64": "0.20.1" + "@esbuild/aix-ppc64": "0.21.3", + "@esbuild/android-arm": "0.21.3", + "@esbuild/android-arm64": "0.21.3", + "@esbuild/android-x64": "0.21.3", + "@esbuild/darwin-arm64": "0.21.3", + "@esbuild/darwin-x64": "0.21.3", + "@esbuild/freebsd-arm64": "0.21.3", + "@esbuild/freebsd-x64": "0.21.3", + "@esbuild/linux-arm": "0.21.3", + "@esbuild/linux-arm64": "0.21.3", + "@esbuild/linux-ia32": "0.21.3", + "@esbuild/linux-loong64": "0.21.3", + "@esbuild/linux-mips64el": "0.21.3", + "@esbuild/linux-ppc64": "0.21.3", + "@esbuild/linux-riscv64": "0.21.3", + "@esbuild/linux-s390x": "0.21.3", + "@esbuild/linux-x64": "0.21.3", + "@esbuild/netbsd-x64": "0.21.3", + "@esbuild/openbsd-x64": "0.21.3", + "@esbuild/sunos-x64": "0.21.3", + "@esbuild/win32-arm64": "0.21.3", + "@esbuild/win32-ia32": "0.21.3", + "@esbuild/win32-x64": "0.21.3" } }, "node_modules/esbuild-wasm": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.20.1.tgz", - "integrity": "sha512-6v/WJubRsjxBbQdz6izgvx7LsVFvVaGmSdwrFHmEzoVgfXL89hkKPoQHsnVI2ngOkcBUQT9kmAM1hVL1k/Av4A==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.21.3.tgz", + "integrity": "sha512-DMOV+eeVra0yVq3XIojfczdEQsz+RiFnpEj7lqs8Gux9mlTpN7yIbw0a4KzLspn0Uhw6UVEH3nUAidSqc/rcQg==", "dev": true, "bin": { "esbuild": "bin/esbuild" @@ -8074,57 +9148,337 @@ "node": ">=0.8.0" } }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "node_modules/eslint": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.4.0.tgz", + "integrity": "sha512-sjc7Y8cUD1IlwYcTS9qPSvGjAC8Ne9LctpxKKu3x/1IC9bnOg98Zy6GxEJUfr1NojMgVPlyANXYns8oE2c1TAA==", "dev": true, + "peer": true, "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/config-array": "^0.15.1", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.4.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.0.1", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" + "eslint": "bin/eslint.js" }, "engines": { - "node": ">=6.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "optionalDependencies": { - "source-map": "~0.6.1" + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, - "optional": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" + }, "engines": { - "node": ">=0.10.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } } }, "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", + "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", "dev": true, + "peer": true, "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-scope/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", "dev": true, + "peer": true, "engines": { - "node": ">=4.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "peer": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "peer": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "peer": true + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "peer": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "peer": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "peer": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", + "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", + "dev": true, + "peer": true, + "dependencies": { + "acorn": "^8.11.3", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/esprima": { @@ -8140,6 +9494,19 @@ "node": ">=4" } }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "peer": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -8215,23 +9582,23 @@ } }, "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": ">=10" + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" @@ -8444,6 +9811,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -8466,6 +9839,13 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "peer": true + }, "node_modules/fastparse": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", @@ -8502,19 +9882,17 @@ "pend": "~1.2.0" } }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, + "peer": true, "dependencies": { - "escape-string-regexp": "^1.0.5" + "flat-cache": "^4.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=16.0.0" } }, "node_modules/fill-range": { @@ -8600,6 +9978,20 @@ "flat": "cli.js" } }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "peer": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/flatted": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", @@ -8636,9 +10028,9 @@ } }, "node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", "dev": true, "dependencies": { "cross-spawn": "^7.0.0", @@ -8728,18 +10120,26 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/fs-monkey": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", - "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", - "dev": true - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -8863,32 +10263,16 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-uri": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", - "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", - "dev": true, - "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4", - "fs-extra": "^11.2.0" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/glob": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", - "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", + "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { @@ -8919,6 +10303,30 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -9056,14 +10464,6 @@ "node": ">=0.10.0" } }, - "node_modules/hark": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/hark/-/hark-1.2.3.tgz", - "integrity": "sha512-u68vz9SCa38ESiFJSDjqK8XbXqWzyot7Cj6Y2b6jk2NJ+II3MY2dIrLMg/kjtIAun4Y1DHF/20hfx4rq1G5GMg==", - "dependencies": { - "wildemitter": "^1.2.0" - } - }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -9374,27 +10774,20 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.0.tgz", + "integrity": "sha512-36AV1fIaI2cWRzHo+rbcxhe3M3jUDCNzc4D5zRl57sEWRAxdXYtw7FSQKYY6PDKssiAKjLYypbssHk+xs/kMXw==", "dev": true, "dependencies": { - "@types/http-proxy": "^1.17.8", + "@types/http-proxy": "^1.17.10", + "debug": "^4.3.4", "http-proxy": "^1.18.1", "is-glob": "^4.0.1", "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" + "micromatch": "^4.0.5" }, "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/http-server": { @@ -9508,12 +10901,36 @@ } }, "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", "dev": true, "engines": { - "node": ">=10.17.0" + "node": ">=14.18.0" + } + }, + "node_modules/husky": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "dev": true, + "engines": { + "node": ">=10.18" } }, "node_modules/i18next": { @@ -9592,6 +11009,12 @@ "node": ">= 4" } }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, "node_modules/ignore-walk": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", @@ -9604,6 +11027,30 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/image-size": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", @@ -9686,7 +11133,8 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/ini": { "version": "4.1.2", @@ -9707,18 +11155,18 @@ } }, "node_modules/inquirer": { - "version": "9.2.15", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.15.tgz", - "integrity": "sha512-vI2w4zl/mDluHt9YEQ/543VTCwPKWiHzKtm9dM2V0NdFcqEexDAjUHzO1oA60HRNaVifGXXM1tRRNluLVHa0Kg==", + "version": "9.2.22", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.22.tgz", + "integrity": "sha512-SqLLa/Oe5rZUagTR9z+Zd6izyatHglbmbvVofo1KzuVB54YHleWzeHNLoR7FOICGOeQSqeLh1cordb3MzhGcEw==", "dev": true, "dependencies": { - "@ljharb/through": "^2.3.12", + "@inquirer/figures": "^1.0.2", + "@ljharb/through": "^2.3.13", "ansi-escapes": "^4.3.2", "chalk": "^5.3.0", "cli-cursor": "^3.1.0", "cli-width": "^4.1.0", "external-editor": "^3.1.0", - "figures": "^3.2.0", "lodash": "^4.17.21", "mute-stream": "1.0.0", "ora": "^5.4.1", @@ -9904,15 +11352,12 @@ } }, "node_modules/is-core-module": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz", - "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9973,12 +11418,15 @@ } }, "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-glob": { @@ -9993,6 +11441,39 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", @@ -10026,6 +11507,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-network-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", + "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -10050,6 +11543,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", @@ -10106,12 +11609,12 @@ } }, "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -10363,9 +11866,9 @@ } }, "node_modules/jackspeak": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", - "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", + "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -10434,9 +11937,9 @@ } }, "node_modules/jiti": { - "version": "1.21.6", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", - "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", "dev": true, "bin": { "jiti": "bin/jiti.js" @@ -10479,6 +11982,13 @@ "node": ">=4" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "peer": true + }, "node_modules/json-parse-even-better-errors": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", @@ -10494,6 +12004,13 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "peer": true + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -10667,16 +12184,6 @@ "url": "https://github.com/sponsors/mattlewis92" } }, - "node_modules/karma-coverage-istanbul-reporter/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/karma-coverage-istanbul-reporter/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -10736,18 +12243,6 @@ "node": ">=6" } }, - "node_modules/karma-coverage-istanbul-reporter/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/karma-coverage-istanbul-reporter/node_modules/rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -10779,28 +12274,6 @@ "node": ">=0.10.0" } }, - "node_modules/karma-coverage/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/karma-coverage/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/karma-jasmine": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.1.tgz", @@ -10914,16 +12387,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/karma/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/karma/node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -10986,18 +12449,6 @@ "node": ">=4.0.0" } }, - "node_modules/karma/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/karma/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -11057,6 +12508,16 @@ "integrity": "sha512-i/XBRTiLqRConPKioy2oq45vbv04e8x59b0mnsIRQM+7Ec/8BC7UcL5pnC4FMeGb8KwG7q4wOMw7CtNZf5tiIg==", "dev": true }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "peer": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -11066,19 +12527,10 @@ "node": ">=0.10.0" } }, - "node_modules/klona": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", - "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/launch-editor": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.8.0.tgz", - "integrity": "sha512-vJranOAJrI/llyWGRQqiDM+adrw+k83fvmmx3+nV47g3+36xM15jE+zyZ6Ffel02+xSvuM0b2GDRosXZkbb6wA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", + "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", "dev": true, "dependencies": { "picocolors": "^1.0.0", @@ -11112,23 +12564,29 @@ } }, "node_modules/less-loader": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.0.tgz", - "integrity": "sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug==", + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-12.2.0.tgz", + "integrity": "sha512-MYUxjSQSBUQmowc0l5nPieOYwMzGPUaTzB6inNW/bdPEG9zOL3eAAD1Qw5ZxSPk7we5dMojHwNODYMV1hq4EVg==", "dev": true, - "dependencies": { - "klona": "^2.0.4" - }, "engines": { - "node": ">= 14.15.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { + "@rspack/core": "0.x || 1.x", "less": "^3.5.0 || ^4.0.0", "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, "node_modules/less/node_modules/make-dir": { @@ -11165,6 +12623,20 @@ "node": ">=0.10.0" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/license-webpack-plugin": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", @@ -11191,12 +12663,221 @@ "immediate": "~3.0.5" } }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/lint-staged": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-14.0.1.tgz", + "integrity": "sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw==", + "dev": true, + "dependencies": { + "chalk": "5.3.0", + "commander": "11.0.0", + "debug": "4.3.4", + "execa": "7.2.0", + "lilconfig": "2.1.0", + "listr2": "6.6.1", + "micromatch": "4.0.5", + "pidtree": "0.6.0", + "string-argv": "0.3.2", + "yaml": "2.3.1" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/lint-staged/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/lint-staged/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/lint-staged/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/listr2": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", + "integrity": "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==", + "dev": true, + "dependencies": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^5.0.1", + "rfdc": "^1.3.0", + "wrap-ansi": "^8.1.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/listr2/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, + "node_modules/listr2/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/livekit-client": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/livekit-client/-/livekit-client-2.1.0.tgz", @@ -11217,6 +12898,37 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, + "node_modules/lmdb": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.0.8.tgz", + "integrity": "sha512-9rp8JT4jPhCRJUL7vRARa2N06OLSYzLwQsEkhC6Qu5XbcLyM/XBLMzDlgS/K7l7c5CdURLdDk9uE+hPFIogHTQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "msgpackr": "^1.9.9", + "node-addon-api": "^6.1.0", + "node-gyp-build-optional-packages": "5.1.1", + "ordered-binary": "^1.4.1", + "weak-lru-cache": "^1.2.2" + }, + "bin": { + "download-lmdb-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@lmdb/lmdb-darwin-arm64": "3.0.8", + "@lmdb/lmdb-darwin-x64": "3.0.8", + "@lmdb/lmdb-linux-arm": "3.0.8", + "@lmdb/lmdb-linux-arm64": "3.0.8", + "@lmdb/lmdb-linux-x64": "3.0.8", + "@lmdb/lmdb-win32-x64": "3.0.8" + } + }, + "node_modules/lmdb/node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -11259,6 +12971,13 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "peer": true + }, "node_modules/log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", @@ -11271,6 +12990,192 @@ "node": ">=4" } }, + "node_modules/log-update": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", + "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", + "dev": true, + "dependencies": { + "ansi-escapes": "^5.0.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^5.0.0", + "strip-ansi": "^7.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "dev": true, + "dependencies": { + "type-fest": "^1.0.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dev": true, + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/log-update/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/log-update/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/log-update/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/log4js": { "version": "6.9.1", "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", @@ -11342,15 +13247,12 @@ } }, "node_modules/magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" } }, "node_modules/make-dir": { @@ -11397,15 +13299,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/make-fetch-happen/node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/map-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", @@ -11434,15 +13327,22 @@ } }, "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.9.2.tgz", + "integrity": "sha512-f16coDZlTG1jskq3mxarwB+fGRrd0uXWt+o1WIhRfOwbXQZqUDsTVxQBFK9JjRQHblg8eAG2JSbprDXKjc7ijQ==", "dev": true, "dependencies": { - "fs-monkey": "^1.0.4" + "@jsonjoy.com/json-pack": "^1.0.3", + "@jsonjoy.com/util": "^1.1.2", + "sonic-forest": "^1.0.0", + "tslib": "^2.0.0" }, "engines": { "node": ">= 4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" } }, "node_modules/merge-descriptors": { @@ -11534,18 +13434,21 @@ } }, "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/mini-css-extract-plugin": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz", - "integrity": "sha512-/1HDlyFRxWIZPI1ZpgqlZ8jMw/1Dp/dl3P0L1jtZ+zVcHqwPhGwaJwKL00WVgfnBy6PWCde9W65or7IIETImuA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", + "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", "dev": true, "dependencies": { "schema-utils": "^4.0.0", @@ -11569,18 +13472,15 @@ "dev": true }, "node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" } }, "node_modules/minimist": { @@ -11864,16 +13764,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/mocha/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12253,7 +14143,51 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/msgpackr": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.10.2.tgz", + "integrity": "sha512-L60rsPynBvNE+8BWipKKZ9jHcSGbtyJYIwjRq0VrIvQ08cRjntGXJYW/tmciZ2IHWIY8WEW32Qa2xbh5+SKBZA==", + "dev": true, + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz", + "integrity": "sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.0.7" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.2", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.2", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.2", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.2", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.2", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.2" + } + }, + "node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz", + "integrity": "sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==", + "dev": true, + "optional": true, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } }, "node_modules/multicast-dns": { "version": "7.2.5", @@ -12289,6 +14223,13 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "peer": true + }, "node_modules/needle": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", @@ -12334,24 +14275,15 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/ng-packagr": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-17.3.0.tgz", - "integrity": "sha512-kMSqxeDgv88SWCoapWNRRN1UdBgwu9/Pw/j7u2WFGmzrIWUFivNWBBSSL94kMxr2La+Z9wMwiL8EwKNvmCpg2A==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-18.0.0.tgz", + "integrity": "sha512-fIkMk2nOAuhsLGOiCQUVdXpOI2WUdnMX/u8VXMRWVD0i+nLJdcWb1mmRb4TAYgqimy7M47OgQFKQrv/SBMgqGQ==", "dev": true, "dependencies": { - "@rollup/plugin-json": "^6.0.1", + "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/wasm-node": "^4.5.0", + "@rollup/wasm-node": "^4.18.0", "ajv": "^8.12.0", "ansi-colors": "^4.1.3", "browserslist": "^4.22.1", @@ -12360,7 +14292,7 @@ "commander": "^12.0.0", "convert-source-map": "^2.0.0", "dependency-graph": "^1.0.0", - "esbuild-wasm": "^0.20.0", + "esbuild": "^0.21.3", "fast-glob": "^3.3.1", "find-cache-dir": "^3.3.2", "injection-js": "^2.4.0", @@ -12376,17 +14308,16 @@ "ng-packagr": "cli/main.js" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || >=20.11.1" }, "optionalDependencies": { - "esbuild": "^0.20.0", - "rollup": "^4.5.0" + "rollup": "^4.18.0" }, "peerDependencies": { - "@angular/compiler-cli": "^17.0.0 || ^17.2.0-next.0 || ^17.3.0-next.0", + "@angular/compiler-cli": "^18.0.0-next.0 || ^18.1.0-next.0", "tailwindcss": "^2.0.0 || ^3.0.0", "tslib": "^2.3.0", - "typescript": ">=5.2 <5.5" + "typescript": ">=5.4 <5.5" }, "peerDependenciesMeta": { "tailwindcss": { @@ -12529,6 +14460,20 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", + "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, "node_modules/node-gyp/node_modules/isexe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", @@ -12538,6 +14483,15 @@ "node": ">=16" } }, + "node_modules/node-gyp/node_modules/proc-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", + "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/node-gyp/node_modules/which": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", @@ -12582,6 +14536,52 @@ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, + "node_modules/nodemon": { + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", + "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/nodemon/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/nopt": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", @@ -12664,13 +14664,13 @@ } }, "node_modules/npm-package-arg": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz", - "integrity": "sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.2.tgz", + "integrity": "sha512-IGN0IAwmhDJwy13Wc8k+4PEbTPhpJnMtfR53ZbOyjkvmEcLS4nCwp6mvMWjS5sUjeiW3mpx6cHmuhKEu9XmcQw==", "dev": true, "dependencies": { "hosted-git-info": "^7.0.0", - "proc-log": "^3.0.0", + "proc-log": "^4.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^5.0.0" }, @@ -12691,9 +14691,9 @@ } }, "node_modules/npm-pick-manifest": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.0.0.tgz", - "integrity": "sha512-VfvRSs/b6n9ol4Qb+bDwNGUXutpy76x6MARw/XssevE0TnctIKcmklJZM5Z7nqs5z5aW+0S63pgCNbpkUNNXBg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.0.1.tgz", + "integrity": "sha512-Udm1f0l2nXb3wxDpKjfohwgdFUSV50UVwzEIpDXVsbDMXVIEF81a/i0UhuQbhrPMMmdiq3+YMFLFIRVLs3hxQw==", "dev": true, "dependencies": { "npm-install-checks": "^6.0.0", @@ -12706,12 +14706,12 @@ } }, "node_modules/npm-registry-fetch": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-16.2.1.tgz", - "integrity": "sha512-8l+7jxhim55S85fjiDGJ1rZXBWGtRLi1OSb4Z3BPLObPuIaeKRlPRiYMSHU4/81ck3t71Z+UwDDl47gcpmfQQA==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.0.1.tgz", + "integrity": "sha512-fLu9MTdZTlJAHUek/VLklE6EpIiP3VZpTiuN7OOMCt2Sd67NCpSEetMaxHHEZiZxllp8ZLsUpvbEszqTFEc+wA==", "dev": true, "dependencies": { - "@npmcli/redact": "^1.1.0", + "@npmcli/redact": "^2.0.0", "make-fetch-happen": "^13.0.0", "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", @@ -12724,25 +14724,44 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm-registry-fetch/node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, "dependencies": { - "path-key": "^3.0.0" + "path-key": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-watch": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/npm-watch/-/npm-watch-0.11.0.tgz", + "integrity": "sha512-wAOd0moNX2kSA2FNvt8+7ORwYaJpQ1ZoWjUYdb1bBCxq4nkWuU0IiJa9VpVxrj5Ks+FGXQd62OC/Bjk0aSr+dg==", + "dev": true, + "dependencies": { + "nodemon": "^2.0.7", + "through2": "^4.0.2" + }, + "bin": { + "npm-watch": "cli.js" } }, "node_modules/nth-check": { @@ -12855,15 +14874,15 @@ } }, "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "dependencies": { - "mimic-fn": "^2.1.0" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">=6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -12904,44 +14923,22 @@ "opener": "bin/opener-bin.js" } }, - "node_modules/openvidu-browser-v2compatibility": { - "version": "3.0.0-dev5", - "resolved": "https://registry.npmjs.org/openvidu-browser-v2compatibility/-/openvidu-browser-v2compatibility-3.0.0-dev5.tgz", - "integrity": "sha512-p7vUg5rrpRhJ7H2LQ8/9BxSiCXOUiNUrMVMzUo2r+uNYQ1sadd4DWSC//HxVC1i37RPWGhlxWJUhC3zm6hSzoQ==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "peer": true, "dependencies": { - "@livekit/egress-sdk": "^0.2.0", - "@livekit/track-processors": "^0.3.1", - "@mediapipe/tasks-vision": "0.10.12", - "events": "3.3.0", - "hark": "^1.2.3", - "inherits": "2.0.4", - "livekit-client": "2.1.0", - "mime": "3.0.0", - "platform": "1.3.6", - "rxjs": "7.8.1", - "semver": "7.6.0", - "socket.io-client": "^4.7.5", - "uuid": "9.0.1", - "wolfy87-eventemitter": "5.2.9" - } - }, - "node_modules/openvidu-browser-v2compatibility/node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "bin": { - "mime": "cli.js" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/openvidu-browser-v2compatibility/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dependencies": { - "tslib": "^2.1.0" + "node": ">= 0.8.0" } }, "node_modules/ora": { @@ -13053,6 +15050,12 @@ "node": ">=8" } }, + "node_modules/ordered-binary": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.1.tgz", + "integrity": "sha512-5VyHfHY3cd0iza71JepYG50My+YUbrFtGoUz2ooEydPyPM7Aai/JW098juLr+RG6+rDJuzNNTsEQu2DZa1A41A==", + "dev": true + }, "node_modules/os-name": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", @@ -13121,16 +15124,20 @@ } }, "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.0.tgz", + "integrity": "sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA==", "dev": true, "dependencies": { - "@types/retry": "0.12.0", + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", "retry": "^0.13.1" }, "engines": { - "node": ">=8" + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-retry/node_modules/retry": { @@ -13151,71 +15158,32 @@ "node": ">=6" } }, - "node_modules/pac-proxy-agent": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", - "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", - "dev": true, - "dependencies": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.2", - "pac-resolver": "^7.0.0", - "socks-proxy-agent": "^8.0.2" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-resolver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", - "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", - "dev": true, - "dependencies": { - "degenerator": "^5.0.0", - "netmask": "^2.0.2" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "dev": true - }, "node_modules/pacote": { - "version": "17.0.6", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-17.0.6.tgz", - "integrity": "sha512-cJKrW21VRE8vVTRskJo78c/RCvwJCn1f4qgfxL4w77SOWrTCRcmfkYHlHtS0gqpgjv3zhXflRtgsrUCX5xwNnQ==", + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-18.0.6.tgz", + "integrity": "sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==", "dev": true, "dependencies": { "@npmcli/git": "^5.0.0", "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/package-json": "^5.1.0", "@npmcli/promise-spawn": "^7.0.0", - "@npmcli/run-script": "^7.0.0", + "@npmcli/run-script": "^8.0.0", "cacache": "^18.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^11.0.0", "npm-packlist": "^8.0.0", "npm-pick-manifest": "^9.0.0", - "npm-registry-fetch": "^16.0.0", - "proc-log": "^3.0.0", + "npm-registry-fetch": "^17.0.0", + "proc-log": "^4.0.0", "promise-retry": "^2.0.1", - "read-package-json": "^7.0.0", - "read-package-json-fast": "^3.0.0", "sigstore": "^2.2.0", "ssri": "^10.0.0", "tar": "^6.1.11" }, "bin": { - "pacote": "lib/bin.js" + "pacote": "bin/index.js" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -13469,9 +15437,9 @@ "dev": true }, "node_modules/picomatch": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", - "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "engines": { "node": ">=12" @@ -13480,6 +15448,18 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -13490,9 +15470,9 @@ } }, "node_modules/piscina": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.4.0.tgz", - "integrity": "sha512-+AQduEJefrOApE4bV7KRmp3N2JnnyErlVqq4P/jmko4FPz9Z877BCccl/iB3FdrWSUkvbGV9Kan/KllJgat3Vg==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.5.0.tgz", + "integrity": "sha512-iBaLWI56PFP81cfBSomWTmhOo9W2/yhIOL+Tk8O1vBCpK39cM0tGxB+wgYjG31qq4ohGvysfXSdnj8h7g4rZxA==", "dev": true, "optionalDependencies": { "nice-napi": "^1.0.2" @@ -13595,11 +15575,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/platform": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" - }, "node_modules/png-js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", @@ -13639,9 +15614,9 @@ } }, "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "dev": true, "funding": [ { @@ -13660,7 +15635,7 @@ "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" }, "engines": { "node": "^10 || ^12 || >=14" @@ -13799,6 +15774,43 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/prismjs": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", @@ -13809,9 +15821,9 @@ } }, "node_modules/proc-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", - "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", + "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -13873,34 +15885,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-agent": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", - "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", - "dev": true, - "dependencies": { - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.3", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.1", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.2" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -13923,6 +15907,12 @@ "dev": true, "optional": true }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -13934,10 +15924,13 @@ } }, "node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } }, "node_modules/qjobs": { "version": "1.2.0", @@ -14016,35 +16009,6 @@ "node": ">= 0.8" } }, - "node_modules/read-package-json": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-7.0.1.tgz", - "integrity": "sha512-8PcDiZ8DXUjLf687Ol4BR8Bpm2umR7vhoZOzNRt+uxD9GpBh/K+CAAALVIiYFknmvlmyg7hM7BSNUXPaCCqd0Q==", - "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", - "dev": true, - "dependencies": { - "glob": "^10.2.2", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/read-package-json-fast": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", - "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", - "dev": true, - "dependencies": { - "json-parse-even-better-errors": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -14286,6 +16250,30 @@ "node": ">=8" } }, + "node_modules/restore-cursor/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/restore-cursor/node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -14312,9 +16300,9 @@ } }, "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", "dev": true }, "node_modules/rimraf": { @@ -14333,16 +16321,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/rimraf/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -14364,18 +16342,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/rollup": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", @@ -14411,6 +16377,18 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-async": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", @@ -14504,9 +16482,9 @@ "integrity": "sha512-LRneZZRXNgjzwG4bDQdOTSbze3fHm1EAKN/8bePxnlEZiBmkYEDggaHbuvHI9/hoqHbGfsEA7tWS9GhYHZBBsw==" }, "node_modules/sass": { - "version": "1.71.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz", - "integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==", + "version": "1.77.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.2.tgz", + "integrity": "sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -14521,9 +16499,9 @@ } }, "node_modules/sass-loader": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.1.1.tgz", - "integrity": "sha512-QX8AasDg75monlybel38BZ49JP5Z+uSKfKwF2rO7S74BywaRmGQMUBw9dtkS+ekyM/QnP+NOrRYq8ABMZ9G8jw==", + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.2.1.tgz", + "integrity": "sha512-G0VcnMYU18a4N7VoNDegg2OuMjYtxnqzQWARVWCIVSZwJeiL9kg8QMsuIZOplsJgTzZLF6jGxI3AClj8I9nRdQ==", "dev": true, "dependencies": { "neo-async": "^2.6.2" @@ -14585,6 +16563,23 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/schema-utils/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/sdp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz", @@ -14611,14 +16606,14 @@ "dev": true }, "node_modules/selenium-webdriver": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.5.0.tgz", - "integrity": "sha512-9mSFii+lRwcnT2KUAB1kqvx6+mMiiQHH60Y0VUtr3kxxi3oZ3CV3B8e2nuJ7T4SPb+Q6VA0swswe7rYpez07Bg==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.12.0.tgz", + "integrity": "sha512-zvPzmTsky6WfO6+BGMj2mCJsw7qKnfQONur2b+pGn8jeTiC+WAUOthZOnaK+HkX5wiU6L4uoMF+JIcOVstp25A==", "dev": true, "dependencies": { - "jszip": "^3.10.0", + "jszip": "^3.10.1", "tmp": "^0.2.1", - "ws": ">=8.7.0" + "ws": ">=8.13.0" }, "engines": { "node": ">= 14.20.0" @@ -14647,12 +16642,10 @@ } }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, "bin": { "semver": "bin/semver.js" }, @@ -14678,22 +16671,6 @@ "semver": "bin/semver" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -14992,6 +16969,27 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "dev": true, + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/sirv": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", @@ -15018,6 +17016,34 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -15047,33 +17073,41 @@ } }, "node_modules/socket.io-adapter": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", - "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz", + "integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==", "dev": true, "dependencies": { "debug": "~4.3.4", - "ws": "~8.17.1" + "ws": "~8.11.0" } }, - "node_modules/socket.io-client": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", - "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.2", - "engine.io-client": "~6.5.2", - "socket.io-parser": "~4.2.4" - }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "dev": true, "engines": { "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" @@ -15130,6 +17164,25 @@ "node": ">= 14" } }, + "node_modules/sonic-forest": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sonic-forest/-/sonic-forest-1.0.3.tgz", + "integrity": "sha512-dtwajos6IWMEWXdEbW1IkEkyL2gztCAgDplRIX+OT5aRKnEd5e7r7YCxRgXZdhRP1FBdOBf8axeTPhzDv8T4wQ==", + "dev": true, + "dependencies": { + "tree-dump": "^1.0.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, "node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -15385,6 +17438,15 @@ } ] }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -15414,6 +17476,24 @@ "node": ">=8" } }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -15489,12 +17569,15 @@ } }, "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/strip-json-comments": { @@ -15548,6 +17631,28 @@ "node": ">=0.10" } }, + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/synckit/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/tablesort": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/tablesort/-/tablesort-5.3.0.tgz", @@ -15659,9 +17764,9 @@ } }, "node_modules/terser": { - "version": "5.29.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.1.tgz", - "integrity": "sha512-lZQ/fyaIGxsbGxApKmoPTODIzELy3++mXhS5hOqaAWZjQtpq/hFHAc+rm29NND1rYRxRWKcjuARNwULNXa5RtQ==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz", + "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -15779,16 +17884,6 @@ "node": ">=8" } }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/test-exclude/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -15810,16 +17905,23 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "peer": true + }, + "node_modules/thingies": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", + "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, "engines": { - "node": "*" + "node": ">=10.18" + }, + "peerDependencies": { + "tslib": "^2" } }, "node_modules/through": { @@ -15828,6 +17930,15 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "dependencies": { + "readable-stream": "3" + } + }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -15891,6 +18002,15 @@ "node": ">=6" } }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, "node_modules/traverse": { "version": "0.6.9", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.9.tgz", @@ -15908,6 +18028,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tree-dump": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.1.tgz", + "integrity": "sha512-WCkcRBVPSlHHq1dc/px9iOfqklvzCbdRwvlNfxGZsrHqf6aZttfPrd7DJTt6oR10dwUfpFFQeVTkPbBIZxX/YA==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -16018,16 +18154,6 @@ "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" } }, - "node_modules/tslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/tslint/node_modules/builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", @@ -16073,18 +18199,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/tslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/tslint/node_modules/semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", @@ -16132,6 +18246,19 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -16310,9 +18437,9 @@ } }, "node_modules/uglify-js": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.18.0.tgz", - "integrity": "sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==", + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "dev": true, "optional": true, "bin": { @@ -16337,13 +18464,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, "node_modules/undici": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.11.1.tgz", - "integrity": "sha512-KyhzaLJnV1qa3BSHdj4AZ2ndqI0QWPxYzaIOio0WzcEJB9gvuysprJSLtpvc2D9mhR9jPDUk7xlJlZbH2KR5iw==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.18.0.tgz", + "integrity": "sha512-nT8jjv/fE9Et1ilR6QoW8ingRTY2Pp4l2RUrdzV5Yz35RJDrtPc1DXvuNqcpsJSGIRHFdt3YKKktTzJA6r0fTA==", "dev": true, "engines": { - "node": ">=18.0" + "node": ">=18.17" } }, "node_modules/undici-types": { @@ -16517,15 +18650,6 @@ "punycode": "^2.1.0" } }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", @@ -16551,6 +18675,7 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -16602,14 +18727,14 @@ } }, "node_modules/vite": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.7.tgz", - "integrity": "sha512-sgnEEFTZYMui/sTlH1/XEnVNHMujOahPLGMxn1+5sIT45Xjng1Ec1K78jRP15dSmVgg5WBin9yO81j3o9OxofA==", + "version": "5.2.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz", + "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==", "dev": true, "dependencies": { - "esbuild": "^0.19.3", - "postcss": "^8.4.35", - "rollup": "^4.2.0" + "esbuild": "^0.20.1", + "postcss": "^8.4.38", + "rollup": "^4.13.0" }, "bin": { "vite": "bin/vite.js" @@ -16656,10 +18781,266 @@ } } }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", "cpu": [ "x64" ], @@ -16672,10 +19053,106 @@ "node": ">=12" } }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/vite/node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", "dev": true, "hasInstallScript": true, "bin": { @@ -16685,29 +19162,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" } }, "node_modules/void-elements": { @@ -16720,9 +19197,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", "dev": true, "dependencies": { "glob-to-regexp": "^0.4.1", @@ -16750,27 +19227,33 @@ "defaults": "^1.0.3" } }, + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", + "dev": true + }, "node_modules/webpack": { - "version": "5.90.3", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.3.tgz", - "integrity": "sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==", + "version": "5.91.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", + "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", "acorn-import-assertions": "^1.9.0", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", + "enhanced-resolve": "^5.16.0", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", @@ -16778,7 +19261,7 @@ "schema-utils": "^3.2.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.0", + "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "bin": { @@ -16845,9 +19328,9 @@ } }, "node_modules/webpack-bundle-analyzer/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "dev": true, "engines": { "node": ">=8.3.0" @@ -16866,19 +19349,20 @@ } }, "node_modules/webpack-dev-middleware": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.1.2.tgz", - "integrity": "sha512-Wu+EHmX326YPYUpQLKmKbTyZZJIB8/n6R09pTmB03kJmnMsVPTo9COzHZFr01txwaCAuZvfBJE4ZCHRcKs5JaQ==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.2.1.tgz", + "integrity": "sha512-hRLz+jPQXo999Nx9fXVdKlg/aehsw1ajA9skAneGmT03xwmyuhvF93p6HUKKbWhXdcERtGTzUCtIQr+2IQegrA==", "dev": true, "dependencies": { "colorette": "^2.0.10", - "memfs": "^3.4.12", + "memfs": "^4.6.0", "mime-types": "^2.1.31", + "on-finished": "^2.4.1", "range-parser": "^1.2.1", "schema-utils": "^4.0.0" }, "engines": { - "node": ">= 14.15.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", @@ -16893,55 +19377,67 @@ } } }, - "node_modules/webpack-dev-server": { - "version": "4.15.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", - "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", + "node_modules/webpack-dev-middleware/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "dependencies": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.5", + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webpack-dev-server": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.0.4.tgz", + "integrity": "sha512-dljXhUgx3HqKP2d8J/fUMvhxGhzjeNVarDLcbO/EWMSgRizDkxHQDZQaLFL5VJY9tRBj2Gz+rvCEYYvhbqPHNA==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", "colorette": "^2.0.10", "compression": "^1.7.4", "connect-history-api-fallback": "^2.0.0", "default-gateway": "^6.0.3", "express": "^4.17.3", "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", + "html-entities": "^2.4.0", "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "launch-editor": "^2.6.0", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.1.1", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "rimraf": "^5.0.5", + "schema-utils": "^4.2.0", + "selfsigned": "^2.4.1", "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.13.0" + "webpack-dev-middleware": "^7.1.0", + "ws": "^8.16.0" }, "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" + "webpack": "^5.0.0" }, "peerDependenciesMeta": { "webpack": { @@ -16952,36 +19448,91 @@ } } }, - "node_modules/webpack-dev-server/node_modules/@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "node_modules/webpack-dev-server/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", - "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", - "dev": true, - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, "engines": { - "node": ">= 12.13.0" + "node": ">=12" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" }, "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", + "dev": true, + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/rimraf": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.7.tgz", + "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==", + "dev": true, + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/webpack-merge": { @@ -17053,6 +19604,28 @@ "ajv": "^6.9.1" } }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/webpack/node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -17198,11 +19771,6 @@ "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", "dev": true }, - "node_modules/wildemitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/wildemitter/-/wildemitter-1.2.1.tgz", - "integrity": "sha512-UMmSUoIQSir+XbBpTxOTS53uJ8s/lVhADCkEbhfRjUGFDPme/XGOb0sBWLx5sTz7Wx/2+TlAw1eK9O5lw5PiEw==" - }, "node_modules/windows-release": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", @@ -17250,16 +19818,78 @@ "node": ">=8.12.0" } }, + "node_modules/windows-release/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/windows-release/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/windows-release/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/windows-release/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/windows-release/node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, - "node_modules/wolfy87-eventemitter": { - "version": "5.2.9", - "resolved": "https://registry.npmjs.org/wolfy87-eventemitter/-/wolfy87-eventemitter-5.2.9.tgz", - "integrity": "sha512-P+6vtWyuDw+MB01X7UeF8TaHBvbCovf4HPEMF/SV7BdDc1SMTiBy13SRD71lQh4ExFTG1d/WNzDGDCyOKSMblw==" + "node_modules/windows-release/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } }, "node_modules/wordwrap": { "version": "1.0.0", @@ -17378,9 +20008,10 @@ "dev": true }, "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", + "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "dev": true, "engines": { "node": ">=10.0.0" }, @@ -17415,14 +20046,6 @@ "sax": "^1.2.4" } }, - "node_modules/xmlhttprequest-ssl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", - "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -17438,6 +20061,15 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -17548,9 +20180,9 @@ "dev": true }, "node_modules/zone.js": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.7.tgz", - "integrity": "sha512-0w6DGkX2BPuiK/NLf+4A8FLE43QwBfuqz2dVgi/40Rj1WmqUskCqj329O/pwrqFJLG5X8wkeG2RhIAro441xtg==" + "version": "0.14.6", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.6.tgz", + "integrity": "sha512-vyRNFqofdaHVdWAy7v3Bzmn84a1JHWSjpuTZROT/uYn8I3p2cmo7Ro9twFmYRQDPhiYOV7QLk0hhY4JJQVqS6Q==" } } } diff --git a/openvidu-components-angular/package.json b/openvidu-components-angular/package.json index 867e2fcb..4638d660 100644 --- a/openvidu-components-angular/package.json +++ b/openvidu-components-angular/package.json @@ -1,39 +1,44 @@ { "dependencies": { - "@angular/animations": "17.3.10", - "@angular/cdk": "17.3.10", - "@angular/common": "17.3.10", - "@angular/core": "17.3.10", - "@angular/flex-layout": "15.0.0-beta.42", - "@angular/forms": "17.3.10", - "@angular/material": "17.3.10", - "@angular/platform-browser": "17.3.10", - "@angular/platform-browser-dynamic": "17.3.10", - "@angular/router": "17.3.10", + "@angular/animations": "18.0.0", + "@angular/cdk": "18.0.0", + "@angular/common": "18.0.0", + "@angular/core": "18.0.0", + "@angular/forms": "18.0.0", + "@angular/material": "18.0.0", + "@angular/platform-browser": "18.0.0", + "@angular/platform-browser-dynamic": "18.0.0", + "@angular/router": "18.0.0", + "@livekit/track-processors": "0.3.2", "autolinker": "4.0.0", - "openvidu-browser-v2compatibility": "^3.0.0-dev5", + "livekit-client": "2.1.0", "rxjs": "7.5.7", "tslib": "2.3.1", "zone.js": "^0.14.6" }, "devDependencies": { - "@angular-devkit/build-angular": "17.3.8", - "@angular/cli": "17.3.8", - "@angular/compiler": "17.3.10", - "@angular/compiler-cli": "17.3.10", - "@angular/elements": "17.3.10", - "@compodoc/compodoc": "^1.1.19", - "@types/chai": "4.3.0", + "@angular-devkit/build-angular": "18.0.1", + "@angular/cli": "18.0.1", + "@angular/compiler": "18.0.0", + "@angular/compiler-cli": "18.0.0", + "@angular/elements": "18.0.0", + "@compodoc/compodoc": "^1.1.25", + "@types/chai": "4.3.6", + "@types/dom-mediacapture-transform": "0.1.9", + "@types/dom-webcodecs": "0.1.11", "@types/mocha": "9.1.0", - "@types/node": "20.12.12", - "@types/selenium-webdriver": "4.1.5", - "@types/ws": "8.5.4", + "@types/node": "20.12.7", + "@types/selenium-webdriver": "4.1.16", + "@types/ws": "^8.5.4", "chai": "4.3.6", - "chromedriver": "122.0.5", + "chromedriver": "116.0.0", "codelyzer": "6.0.2", "concat": "^1.0.3", "cross-env": "^7.0.3", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", "http-server": "14.1.1", + "husky": "^8.0.3", "jasmine-core": "3.10.1", "jasmine-spec-reporter": "7.0.0", "karma": "^6.3.9", @@ -45,38 +50,55 @@ "karma-junit-reporter": "2.0.1", "karma-mocha-reporter": "2.2.5", "karma-notify-reporter": "1.3.0", + "lint-staged": "^14.0.1", "mocha": "9.2.2", - "ng-packagr": "17.3.0", - "selenium-webdriver": "4.5.0", + "ng-packagr": "18.0.0", + "npm-watch": "^0.11.0", + "prettier": "3.0.3", + "selenium-webdriver": "4.12.0", "ts-node": "10.4.0", "tslint": "6.1.3", - "typescript": "^5.4.5", + "typescript": "5.4.5", "webpack-bundle-analyzer": "^4.5.0" }, "name": "openvidu-components-testapp", "private": true, + "watch": { + "doc:serve": { + "patterns": [ + "projects", + "src" + ], + "extensions": "ts,html,scss,css,md", + "quiet": false + } + }, "scripts": { + "husky": "cd .. && husky install", "build": "ng build openvidu-components-testapp --configuration production", "bundle-report": "ng build openvidu-webcomponent --stats-json --configuration production && webpack-bundle-analyzer dist/openvidu-webcomponent/stats.json", - "doc:build": "npx compodoc -c ./projects/openvidu-angular/doc/.compodocrc.json", - "doc:clean-copy": "rm -rf ../../openvidu.io-docs/docs/api/openvidu-angular && cp -r ./docs/openvidu-angular/ ../../openvidu.io-docs/docs/api/openvidu-angular", - "doc:serve": "npx compodoc --watch --serve -c ./projects/openvidu-angular/doc/.compodocrc.json", - "lib:build": "ng build openvidu-angular --configuration production && cd ./dist/openvidu-angular", - "lib:copy": "cp dist/openvidu-angular/openvidu-angular-*.tgz ../../openvidu-tutorials/openvidu-call/openvidu-call-front", + "doc:build": "npx compodoc -c ./projects/openvidu-components-angular/doc/.compodocrc.json", + "doc:generate-directives-tutorials": "node ./projects/openvidu-components-angular/doc/scripts/generate-directive-tutorials.js", + "doc:generate-directive-tables": "node ./projects/openvidu-components-angular/doc/scripts/generate-directive-tables.js", + "doc:clean-copy": "rm -rf ../../openvidu-docs/docs/docs/reference-docs/openvidu-components-angular && cp -r ./docs/openvidu-components-angular/ ../../openvidu-docs/docs/docs/reference-docs/openvidu-components-angular", + "doc:serve": "npx compodoc -c ../openvidu-components-angular/projects/openvidu-components-angular/doc/.compodocrc.json --serve --port 7000", + "doc:serve-watch": "npm-watch doc:serve", + "lib:build": "ng build openvidu-components-angular --configuration production && cd ./dist/openvidu-components-angular", + "lib:copy": "cp dist/openvidu-components-angular/openvidu-components-angular-*.tgz ../../openvidu-call-livekit/openvidu-call-front", "lib:e2e": "tsc --project ./e2e && npx mocha --recursive --timeout 30000 ./e2e/dist/angular.test.js", "lib:e2e-ci": "cross-env LAUNCH_MODE=CI npm run lib:e2e", - "lib:serve": "ng build openvidu-angular --watch", - "lib:test": "ng test openvidu-angular --no-watch --code-coverage", + "lib:serve": "ng build openvidu-components-angular --watch", + "lib:test": "ng test openvidu-components-angular --no-watch --code-coverage", "lint": "ng lint", "start": "ng serve --configuration development --open", "start-prod": "npx http-server ./dist/openvidu-components-testapp/ --port 4200", - "start:ssl": "ng serve --ssl --configuration development --host 0.0.0.0 --port 4200 --ssl-cert /path/to/cert.pem --ssl-key /path/to/key.pem", + "start:ssl": "ng serve --ssl --configuration development --host 0.0.0.0 --port 5080", + "webcomponent:testing-build": "./node_modules/@angular/cli/bin/ng.js build openvidu-webcomponent --configuration testing && node ./openvidu-webcomponent-build.js", "webcomponent:build": "./node_modules/@angular/cli/bin/ng.js build openvidu-webcomponent --configuration production && node ./openvidu-webcomponent-build.js", - "webcomponent:e2e": "tsc --project ./e2e && npx mocha --recursive --timeout 30000 ./e2e/dist/webcomponent.test.js", + "webcomponent:e2e": "tsc --project ./e2e && npx mocha --recursive --timeout 30000 ./e2e/dist/webcomponent-e2e/**/*.test.js", "webcomponent:e2e-ci": "cross-env LAUNCH_MODE=CI npm run webcomponent:e2e", - "webcomponent:e2e-pro": "tsc --project ./e2e && npx mocha --recursive --timeout 30000 ./e2e/dist/webcomponent.pro.test.js", - "webcomponent:e2e-pro-ci": "cross-env LAUNCH_MODE=CI npm run webcomponent:e2e-pro", - "webcomponent:serve-testapp": "npx http-server ./e2e/webcomponent-app/ && echo http://localhost:8080/?OV_URL=https://localhost:4443&OV_SECRET=MY_SECRET&prejoin=false" + "webcomponent:serve-testapp": "npx http-server ./e2e/webcomponent-app/ && echo http://localhost:8080/?OV_URL=https://localhost:4443&OV_SECRET=MY_SECRET&prejoin=false", + "simulate:multiparty": "livekit-cli load-test --url ws://localhost:7880 --api-key devkey --api-secret secret --room daily-call --publishers 8 --audio-publishers 8 --identity-prefix Participant --identity publisher" }, "version": "3.0.0-beta1" } diff --git a/openvidu-components-angular/projects/openvidu-angular/README.md b/openvidu-components-angular/projects/openvidu-angular/README.md deleted file mode 100644 index 83d69908..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/README.md +++ /dev/null @@ -1,85 +0,0 @@ -# openvidu-angular-v2compatibility - -OpenVidu Components v2 compatibility layer for Angular. - -[![License badge](https://img.shields.io/badge/license-Apache2-orange.svg)](http://www.apache.org/licenses/LICENSE-2.0) -[![OpenVidu Tests](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml/badge.svg)](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml) -[![OpenVidu Tests](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-components-angular-E2E.yml/badge.svg)](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-components-angular-E2E.yml) -[![Npm version](https://img.shields.io/npm/v/openvidu-angular-v2compatibility?label=npm-version)](https://npmjs.org/package/openvidu-angular-v2compatibility) -[![Npm downloads](https://img.shields.io/npm/dw/openvidu-angular-v2compatibility?label=npm2-downloads)](https://npmjs.org/package/openvidu-angular-v2compatibility) - -**The easier way to build powerful OpenVidu videoconference frontend applications.** - -## Requirements: - -You will need NPM and Angular CLI to serve the Angular app. Check your installation with the following command: - -```bash -npm -v -ng v -``` - -## How to install it - -1. You need to install the openvidu-angular-v2compatibility library in your Angular application: - -``` -npm install openvidu-angular-v2compatibility -``` - -
- -2. Also you need Angular Material. Check the [Angular Material documentation](https://material.angular.io/guide/getting-started) for installing it. - -## Configure it - -You need to import the openvidu-angular-v2compatibility module in your `app.module.ts`: - -```typescript -import { OpenViduAngularConfig, OpenViduAngularModule } from 'openvidu-angular-v2compatibility'; -import { environment } from 'src/environments/environment'; - -const config: OpenViduAngularConfig = { - production: environment.production -}; - -@NgModule({ - imports: [ - ... - OpenViduAngularModule.forRoot(config) - ] -}) -``` - -You can also add the default styles in your `styles.scss` file: - -```css -:root { - --ov-primary-color: #303030; - --ov-secondary-color: #3e3f3f; - --ov-tertiary-color: #598eff; - --ov-warn-color: #eb5144; - --ov-accent-color: #ffae35; - --ov-light-color: #e6e6e6; - - --ov-logo-background-color: #3a3d3d; - - --ov-text-color: #ffffff; - - --ov-panel-text-color: #1d1d1d; - --ov-panel-background: #ffffff; - - --ov-buttons-radius: 50%; - --ov-leave-button-radius: 10px; - --ov-video-radius: 5px; - --ov-panel-radius: 5px; -} -``` - -## How to use it -OpenVidu team has created a few tutorials to help you to start using OpenVidu Angular components. You can find them [here](https://docs.openvidu.io/en/stable/components/#where-to-start). - - -## openvidu-angular-v2compatibility API - -openvidu-angular-v2compatibility API documentation is available [here](https://docs.openvidu.io/en/stable/api/openvidu-angular-v2compatibility/). \ No newline at end of file diff --git a/openvidu-components-angular/projects/openvidu-angular/doc/.compodocrc.json b/openvidu-components-angular/projects/openvidu-angular/doc/.compodocrc.json deleted file mode 100644 index 6b7d89c5..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/doc/.compodocrc.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "OpenVidu Angular Documentation", - "output": "./docs/openvidu-angular", - "hideGenerator": true, - "disableLifeCycleHooks": true, - "disableProtected": true, - "disableInternal": true, - "disablePrivate": true, - "disableCoverage": true, - "disableRoutesGraph": true, - "disableSourceCode": true, - "disableTemplateTab": true, - "disableDomTree": true, - "disableStyleTab": true, - "disableDependencies": true, - "theme": "gitbook", - "customFavicon": "./projects/openvidu-angular/doc/favicon.ico", - "extTheme": "./projects/openvidu-angular/doc/", - "tsconfig": "./projects/openvidu-angular/doc/tsconfig.doc.json" -} diff --git a/openvidu-components-angular/projects/openvidu-angular/package-lock.json b/openvidu-components-angular/projects/openvidu-angular/package-lock.json deleted file mode 100644 index 6ccdd5ef..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/package-lock.json +++ /dev/null @@ -1,403 +0,0 @@ -{ - "name": "openvidu-angular", - "version": "2.30.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "openvidu-angular", - "version": "2.30.0", - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/animations": "^14.0.0", - "@angular/common": "^14.0.0", - "@angular/core": "^14.0.0", - "@angular/flex-layout": "^14.0.0-beta.40", - "@angular/forms": "^14.0.0", - "@angular/material": "^14.0.0", - "autolinker": "^4.0.0", - "buffer": "^6.0.3", - "openvidu-browser": "2.29.0" - } - }, - "node_modules/@angular/animations": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.3.0.tgz", - "integrity": "sha512-QoBcIKy1ZiU+4qJsAh5Ls20BupWiXiZzKb0s6L9/dntPt5Msr4Ao289XR2P6O1L+kTsCprH9Kt41zyGQ/bkRqg==", - "peer": true, - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^14.15.0 || >=16.10.0" - }, - "peerDependencies": { - "@angular/core": "14.3.0" - } - }, - "node_modules/@angular/cdk": { - "version": "14.2.7", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-14.2.7.tgz", - "integrity": "sha512-/tEsYaUbDSnfEmKVvAMramIptmhI67O+9STjOV0i+74XR2NospeK0fkbywIANu1n3w6AHGMotvRWJrjmbCElFg==", - "peer": true, - "dependencies": { - "tslib": "^2.3.0" - }, - "optionalDependencies": { - "parse5": "^5.0.0" - }, - "peerDependencies": { - "@angular/common": "^14.0.0 || ^15.0.0", - "@angular/core": "^14.0.0 || ^15.0.0", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@angular/common": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.3.0.tgz", - "integrity": "sha512-pV9oyG3JhGWeQ+TFB0Qub6a1VZWMNZ6/7zEopvYivdqa5yDLLDSBRWb6P80RuONXyGnM1pa7l5nYopX+r/23GQ==", - "peer": true, - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^14.15.0 || >=16.10.0" - }, - "peerDependencies": { - "@angular/core": "14.3.0", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@angular/core": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.3.0.tgz", - "integrity": "sha512-wYiwItc0Uyn4FWZ/OAx/Ubp2/WrD3EgUJ476y1XI7yATGPF8n9Ld5iCXT08HOvc4eBcYlDfh90kTXR6/MfhzdQ==", - "peer": true, - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^14.15.0 || >=16.10.0" - }, - "peerDependencies": { - "rxjs": "^6.5.3 || ^7.4.0", - "zone.js": "~0.11.4 || ~0.12.0" - } - }, - "node_modules/@angular/flex-layout": { - "version": "14.0.0-beta.41", - "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-14.0.0-beta.41.tgz", - "integrity": "sha512-x1YcxqkdFlcbVXEy9ebCgW/F+7n/MXkEkwEcVEIPf5v5qn7HZsjQxgIj35Lf0amvMyF7h35prpoxO1uX5+ntFg==", - "deprecated": "This package has been deprecated. Please see https://blog.angular.io/modern-css-in-angular-layouts-4a259dca9127", - "peer": true, - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/cdk": "^14.0.0", - "@angular/common": "^14.0.0", - "@angular/core": "^14.0.0", - "@angular/platform-browser": "^14.0.0", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@angular/forms": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.3.0.tgz", - "integrity": "sha512-fBZZC2UFMom2AZPjGQzROPXFWO6kvCsPDKctjJwClVC8PuMrkm+RRyiYRdBbt2qxWHEqOZM2OCQo73xUyZOYHw==", - "peer": true, - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^14.15.0 || >=16.10.0" - }, - "peerDependencies": { - "@angular/common": "14.3.0", - "@angular/core": "14.3.0", - "@angular/platform-browser": "14.3.0", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@angular/material": { - "version": "14.2.7", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-14.2.7.tgz", - "integrity": "sha512-WXHh8pEStpgkXZJmYOg2cI8BSHkV82ET4XTJCNPdveumaCn1UYnaNzsXD13kw5z+zmy8CufhFEzdXTrv/yt7KQ==", - "peer": true, - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/animations": "^14.0.0 || ^15.0.0", - "@angular/cdk": "14.2.7", - "@angular/common": "^14.0.0 || ^15.0.0", - "@angular/core": "^14.0.0 || ^15.0.0", - "@angular/forms": "^14.0.0 || ^15.0.0", - "@angular/platform-browser": "^14.0.0 || ^15.0.0", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@angular/platform-browser": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.3.0.tgz", - "integrity": "sha512-w9Y3740UmTz44T0Egvc+4QV9sEbO61L+aRHbpkLTJdlEGzHByZvxJmJyBYmdqeyTPwc/Zpy7c02frlpfAlyB7A==", - "peer": true, - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^14.15.0 || >=16.10.0" - }, - "peerDependencies": { - "@angular/animations": "14.3.0", - "@angular/common": "14.3.0", - "@angular/core": "14.3.0" - }, - "peerDependenciesMeta": { - "@angular/animations": { - "optional": true - } - } - }, - "node_modules/autolinker": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-4.0.0.tgz", - "integrity": "sha512-fl5Kh6BmEEZx+IWBfEirnRUU5+cOiV0OK7PEt0RBKvJMJ8GaRseIOeDU3FKf4j3CE5HVefcjHmhYPOcaVt0bZw==", - "peer": true, - "dependencies": { - "tslib": "^2.3.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "peer": true - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "peer": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "peer": true, - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/freeice": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/freeice/-/freeice-2.2.2.tgz", - "integrity": "sha512-XNoIxDHufqPIBSLpp4IrFPnoc+hv/0RwdOGhIoggIDC2ZKf5r6OoixbeoFJSmZOAq2aYiEUArhuQ8zVVrM5C4w==", - "peer": true, - "dependencies": { - "normalice": "^1.0.0" - } - }, - "node_modules/hark": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/hark/-/hark-1.2.3.tgz", - "integrity": "sha512-u68vz9SCa38ESiFJSDjqK8XbXqWzyot7Cj6Y2b6jk2NJ+II3MY2dIrLMg/kjtIAun4Y1DHF/20hfx4rq1G5GMg==", - "peer": true, - "dependencies": { - "wildemitter": "^1.2.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "peer": true - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "peer": true - }, - "node_modules/jsnlog": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/jsnlog/-/jsnlog-2.30.0.tgz", - "integrity": "sha512-o3ROQVkhek+dkc7/9TXlB4TNtxUpYsRLOBJHZYk3Vy0B5zRBmfv9tyr56PrjcgEXuy06ARgfLTANY0+ImhzzGA==", - "peer": true - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "peer": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/normalice": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/normalice/-/normalice-1.0.1.tgz", - "integrity": "sha512-wF2/tv9q/K8S+RqCgll5yC6z/zcXNr+rEHfGIw8A6D58vjfJo+kp749MI6cAHv72LE7nwv92Qi6tZhIeMOOJpg==", - "peer": true - }, - "node_modules/openvidu-browser": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/openvidu-browser/-/openvidu-browser-2.29.0.tgz", - "integrity": "sha512-s9BmrUdcs2Aduek7rAvr+Po4MfkHSXevPOQG4Hu0fGCTdDt+XWz4gNweg38MWlUFhZJdmSarWocGhcejIZ9v7A==", - "peer": true, - "dependencies": { - "events": "3.3.0", - "freeice": "2.2.2", - "hark": "1.2.3", - "inherits": "2.0.4", - "jsnlog": "2.30.0", - "mime": "3.0.0", - "platform": "1.3.6", - "semver": "7.3.8", - "uuid": "9.0.0", - "wolfy87-eventemitter": "5.2.9" - } - }, - "node_modules/parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "optional": true, - "peer": true - }, - "node_modules/platform": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", - "peer": true - }, - "node_modules/rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", - "peer": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "peer": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, - "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", - "peer": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/wildemitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/wildemitter/-/wildemitter-1.2.1.tgz", - "integrity": "sha512-UMmSUoIQSir+XbBpTxOTS53uJ8s/lVhADCkEbhfRjUGFDPme/XGOb0sBWLx5sTz7Wx/2+TlAw1eK9O5lw5PiEw==", - "peer": true - }, - "node_modules/wolfy87-eventemitter": { - "version": "5.2.9", - "resolved": "https://registry.npmjs.org/wolfy87-eventemitter/-/wolfy87-eventemitter-5.2.9.tgz", - "integrity": "sha512-P+6vtWyuDw+MB01X7UeF8TaHBvbCovf4HPEMF/SV7BdDc1SMTiBy13SRD71lQh4ExFTG1d/WNzDGDCyOKSMblw==", - "peer": true - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "peer": true - }, - "node_modules/zone.js": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.12.0.tgz", - "integrity": "sha512-XtC+I5dXU14HrzidAKBNMqneIVUykLEAA1x+v4KVrd6AUPWlwYORF8KgsVqvgdHiKZ4BkxxjvYi/ksEixTPR0Q==", - "peer": true, - "dependencies": { - "tslib": "^2.3.0" - } - } - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/package.json b/openvidu-components-angular/projects/openvidu-angular/package.json deleted file mode 100644 index 421a5d20..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "dependencies": { - "tslib": "^2.3.0" - }, - "name": "openvidu-angular-v2compatibility", - "peerDependencies": { - "@angular/animations": "^16.0.0 || ^17.0.0", - "@angular/common": "^16.0.0 || ^17.0.0", - "@angular/core": "^16.0.0 || ^17.0.0", - "@angular/flex-layout": "^15.0.0-beta.42", - "@angular/forms": "^16.0.0 || ^17.0.0", - "@angular/material": "^16.0.0 || ^17.0.0", - "autolinker": "^4.0.0", - "buffer": "^6.0.3", - "openvidu-browser-v2compatibility": "3.0.0-beta1" - }, - "version": "3.0.0-beta1" -} \ No newline at end of file diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/dashboard/dashboard.component.html b/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/dashboard/dashboard.component.html deleted file mode 100644 index 92d5c02e..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/dashboard/dashboard.component.html +++ /dev/null @@ -1,138 +0,0 @@ -
- - {{ 'ADMIN.DASHBOARD' | translate }} -
- -
-
- -
- - -
-
- {{ 'ADMIN.NO_RECORDINGS' | translate }} -
- -
- - -
- -
- - - - -
-
-
-
-
- {{ 'ADMIN.NAME' | translate }}{{ recording.properties?.name || recording.name }} -
-
- {{ 'ADMIN.SESSION' | translate }}{{ recording.sessionId }} -
-
- {{ 'ADMIN.OUTPUT' | translate }}{{ recording.properties?.outputMode || recording.outputMode }} -
-
- {{ 'ADMIN.DATE' | translate }}{{ recording.createdAt | date: 'M/d/yy, H:mm' }} -
-
- {{ 'ADMIN.DURATION' | translate }}{{ recording.duration | duration }} -
-
- {{ 'ADMIN.SIZE' | translate }}{{ recording.size / 1024 / 1024 | number: '1.1-2' }} MBs -
-
- {{ 'ADMIN.STATUS' | translate }}{{ recording.status }} -
-
-
-
-
-
-
-
- - - {{ 'ADMIN.POWERED_BY' | translate }} - OpenVidu - -
diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/dashboard/dashboard.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/dashboard/dashboard.component.ts deleted file mode 100644 index bb167526..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/dashboard/dashboard.component.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { Component, OnInit, Output, EventEmitter, OnDestroy } from '@angular/core'; -import { Subscription } from 'rxjs'; -import { RecordingInfo } from '../../models/recording.model'; -import { ActionService } from '../../services/action/action.service'; -import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; -import { RecordingService } from '../../services/recording/recording.service'; - -@Component({ - selector: 'ov-admin-dashboard', - templateUrl: './dashboard.component.html', - styleUrls: ['./dashboard.component.css'] -}) -export class AdminDashboardComponent implements OnInit, OnDestroy { - - /** - * Provides event notifications that fire when delete recording button has been clicked. - * The recording should be deleted using the REST API. - * @param recordingId - */ - @Output() onDeleteRecordingClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when refresh recordings button has been clicked. - * The recordings should be updated using the REST API. - */ - @Output() onRefreshRecordingsClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when logout button has been clicked. - */ - @Output() onLogoutClicked: EventEmitter = new EventEmitter(); - - /** - * @internal - */ - recordings: RecordingInfo[] = []; - /** - * @internal - */ - sortDescendent = true; - /** - * @internal - */ - sortByLegend = 'Sort by'; - /** - * @internal - */ - searchValue = ''; - private adminSubscription: Subscription; - /** - * @internal - */ - constructor( - private actionService: ActionService, - private recordingService: RecordingService, - private libService: OpenViduAngularConfigService - ) {} - - /** - * @internal - */ - ngOnInit(): void { - this.subscribeToAdminDirectives(); - } - - /** - * @internal - */ - ngOnDestroy() { - if (this.adminSubscription) this.adminSubscription.unsubscribe(); - } - - /** - * @internal - */ - logout() { - this.onLogoutClicked.emit(); - } - - /** - * @internal - */ - sortRecordingsByDate() { - this.recordings.sort((a, b) => { - if (a.createdAt > b.createdAt) { - return this.sortDescendent ? -1 : 1; - } else if (a.createdAt < b.createdAt) { - return this.sortDescendent ? 1 : -1; - } else { - return 0; - } - }); - this.sortByLegend = 'Date'; - } - - /** - * @internal - */ - sortRecordingsByDuration() { - this.recordings.sort((a, b) => { - if (a.duration > b.duration) { - return this.sortDescendent ? -1 : 1; - } else if (a.duration < b.duration) { - return this.sortDescendent ? 1 : -1; - } else { - return 0; - } - }); - this.sortByLegend = 'Duration'; - } - - /** - * @internal - */ - sortRecordingsBySize() { - this.recordings.sort((a, b) => { - if (a.size > b.size) { - return this.sortDescendent ? -1 : 1; - } else if (a.size < b.size) { - return this.sortDescendent ? 1 : -1; - } else { - return 0; - } - }); - this.sortByLegend = 'Size'; - } - - /** - * @internal - */ - deleteRecording(recordingId: string) { - const succsessCallback = () => { - this.onDeleteRecordingClicked.emit(recordingId); - }; - this.actionService.openDeleteRecordingDialog(succsessCallback); - } - - /** - * @internal - */ - download(recording: RecordingInfo) { - this.recordingService.downloadRecording(recording); - } - - /** - * @internal - */ - refreshRecordings() { - this.onRefreshRecordingsClicked.emit(); - } - - /** - * @internal - */ - async play(recording: RecordingInfo) { - this.recordingService.playRecording(recording); - } - - private subscribeToAdminDirectives() { - this.adminSubscription = this.libService.adminRecordingsListObs.subscribe((recordings: RecordingInfo[]) => { - this.recordings = recordings; - }); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/login/login.component.css b/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/login/login.component.css deleted file mode 100644 index f1e3e744..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/login/login.component.css +++ /dev/null @@ -1,75 +0,0 @@ - -/* TODO(mdc-migration): The following rule targets internal classes of card that may no longer apply for the MDC version. */ -mat-card { - max-width: 220px; - margin: auto; - margin-top: 10vh; -} - -/* TODO(mdc-migration): The following rule targets internal classes of card that may no longer apply for the MDC version. */ -mat-card-content { - margin-bottom: 8px; -} - -/* TODO(mdc-migration): The following rule targets internal classes of card that may no longer apply for the MDC version. */ -mat-card-actions { - padding-top: 0px; -} - -.header { - height: 50px; - background-color: var(--ov-secondary-color); - color: var(--ov-text-color); -} - -mat-spinner { - margin: auto; -} -.mat-mdc-card-actions { - margin: 0; -} - -.full-width { - width: 100%; -} - -.outer { - display: table; - position: absolute; - height: 100%; - width: 100%; -} - -.middle { - display: table-cell; - vertical-align: middle; -} - -.inner { - margin-left: auto; - margin-right: auto; -} - -#login-btn { - text-transform: none; - font-size: 17px; - width: 100%; -} - -::ng-deep .mat-mdc-input-element { - caret-color: #000000; -} -/* TODO(mdc-migration): The following rule targets internal classes of option that may no longer apply for the MDC version. */ -::ng-deep .mat-primary .mat-mdc-option.mat-selected:not(.mat-option-disabled) { - color: #000000; -} - -/* TODO(mdc-migration): The following rule targets internal classes of form-field that may no longer apply for the MDC version. */ -::ng-deep .mat-form-field-label { - color: var(--ov-panel-text-color) !important; -} - -/* TODO(mdc-migration): The following rule targets internal classes of form-field that may no longer apply for the MDC version. */ -::ng-deep .mat-mdc-form-field.mat-focused .mat-form-field-ripple { - background-color: var(--ov-panel-text-color) !important; -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/login/login.component.html b/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/login/login.component.html deleted file mode 100644 index db5eb5da..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/login/login.component.html +++ /dev/null @@ -1,44 +0,0 @@ - - - -
-
-
- -
-
-
- - - -
- - - - -
- - {{ 'ADMIN.SECRET' | translate }} - - {{ 'ADMIN.SECRET_REQURED' | translate }} - -
- -
- -
- - - -
diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/login/login.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/login/login.component.ts deleted file mode 100644 index 4aa69210..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/login/login.component.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { Component, ElementRef, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'; -import { UntypedFormControl, Validators, FormGroupDirective, NgForm } from '@angular/forms'; -import { ErrorStateMatcher } from '@angular/material/core'; -import { Subscription } from 'rxjs'; -import { ActionService } from '../../services/action/action.service'; -import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; - -@Component({ - selector: 'ov-admin-login', - templateUrl: './login.component.html', - styleUrls: ['./login.component.css'] -}) -export class AdminLoginComponent implements OnInit { - /** - * Provides event notifications that fire when login button has been clicked. - * The event will contain the password value. - */ - @Output() onLoginButtonClicked: EventEmitter = new EventEmitter(); - - /** - * @internal - */ - checkingLogged = false; - /** - * @internal - */ - secret: string; - /** - * @internal - */ - showSpinner = false; - - /** - * @internal - */ - loginFormControl = new UntypedFormControl('', [Validators.required]); - /** - * @internal - */ - matcher = new FormErrorStateMatcher(); - - /** - * @internal - */ - @ViewChild('submitBtn') submitBtn: ElementRef; - /** - * @internal - */ - @ViewChild('loginForm', { read: ElementRef }) loginForm: ElementRef; - - private errorSub: Subscription; - - /** - * @internal - */ - constructor(private libService: OpenViduAngularConfigService, private actionService: ActionService) {} - - /** - * @internal - */ - ngOnInit() { - this.subscribeToAdminLoginDirectives(); - } - - /** - * @internal - */ - ngOnDestroy() { - this.showSpinner = false; - if (this.errorSub) this.errorSub.unsubscribe(); - } - - /** - * @internal - */ - login() { - this.showSpinner = true; - this.onLoginButtonClicked.emit(this.secret); - } - - /** - * @internal - */ - submitForm() { - if (this.loginForm.nativeElement.checkValidity()) { - this.login(); - } else { - this.submitBtn.nativeElement.click(); - } - } - - private subscribeToAdminLoginDirectives() { - this.errorSub = this.libService.adminLoginErrorObs.subscribe((value) => { - const errorExists = !!value; - if (errorExists) { - this.showSpinner = false; - this.actionService.openDialog(value.error, value.message, true); - } - }); - } -} -/** - * @internal - */ -export class FormErrorStateMatcher implements ErrorStateMatcher { - isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean { - const isSubmitted = form && form.submitted; - return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted)); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.html b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.html deleted file mode 100644 index 4e92fecb..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.html +++ /dev/null @@ -1,5 +0,0 @@ -
-
-
-
-
diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.ts deleted file mode 100644 index 872b4fd3..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { PublisherSpeakingEvent, StreamManager } from 'openvidu-browser-v2compatibility'; - -/** - * @internal - */ -@Component({ - selector: 'ov-audio-wave', - templateUrl: './audio-wave.component.html', - styleUrls: ['./audio-wave.component.css'] -}) -export class AudioWaveComponent implements OnInit, OnDestroy { - isSpeaking: boolean = false; - - @Input() streamManager: StreamManager; - - constructor() {} - - ngOnInit(): void { - this.subscribeSpeakingEvents(); - } - - ngOnDestroy(): void { - this.unsubscribeSpeakingEvents(); - } - - - private subscribeSpeakingEvents() { - if (this.streamManager) { - this.streamManager.on('publisherStartSpeaking', (event: PublisherSpeakingEvent) => (this.isSpeaking = true)); - this.streamManager.on('publisherStopSpeaking', (event: PublisherSpeakingEvent) => (this.isSpeaking = false)); - } - } - - private unsubscribeSpeakingEvents() { - if (this.streamManager) { - this.streamManager.off('publisherStartSpeaking'); - this.streamManager.off('publisherStopSpeaking'); - } - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/dialogs/recording-dialog.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/dialogs/recording-dialog.component.ts deleted file mode 100644 index e1927a0b..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/dialogs/recording-dialog.component.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Component, Inject } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { RecordingDialogData } from '../../models/dialog.model'; - -/** - * @internal - */ -@Component({ - selector: 'app-recording-dialog', - template: ` -
- -
-
- -
- `, - styles: [ - ` - video { - max-height: 64vh; - max-width: 100%; - } - ` - ] -}) -export class RecordingDialogComponent { - src: string; - - constructor(public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: RecordingDialogData) { - this.src = data.src; - } - close() { - this.dialogRef.close(); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/layout/layout.component.html b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/layout/layout.component.html deleted file mode 100644 index 0ba43776..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/layout/layout.component.html +++ /dev/null @@ -1,18 +0,0 @@ -
-
-
- -
- -
- -
-
- - -
diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/layout/layout.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/layout/layout.component.ts deleted file mode 100644 index a6c95724..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/layout/layout.component.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ContentChild, - OnDestroy, - OnInit, - TemplateRef, - ViewChild, - ViewContainerRef -} from '@angular/core'; -import { Subscription } from 'rxjs'; -import { StreamDirective } from '../../directives/template/openvidu-angular.directive'; -import { ParticipantAbstractModel } from '../../models/participant.model'; -import { LayoutService } from '../../services/layout/layout.service'; -import { ParticipantService } from '../../services/participant/participant.service'; - -/** - * - * The **LayoutComponent** is hosted inside of the {@link VideoconferenceComponent}. - * It is in charge of displaying the participants streams layout. - * - *
- * - *
- *

OpenVidu Angular Directives

- * - * The LayoutComponent can be replaced with a custom component. It provides us the following {@link https://angular.io/guide/structural-directives Angular structural directives} - * for doing this. - * - * | **Directive** | **Reference** | - * |:----------------------------------:|:---------------------------------------------:| - * | ***ovLayout** | {@link LayoutDirective} | - * - *
- * - * It is also providing us a way to **replace the {@link StreamComponent Stream Component}** (which is hosted inside of it) with a custom one. - * It will recognise the following directive in a child element. - * - * - * | **Directive** | **Reference** | - * |:----------------------------------:|:---------------------------------------------:| - * | ***ovStream** | {@link StreamDirective} | - * - * - *
- *
- */ -@Component({ - selector: 'ov-layout', - templateUrl: './layout.component.html', - styleUrls: ['./layout.component.css'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit { - /** - * @ignore - */ - @ContentChild('stream', { read: TemplateRef }) streamTemplate: TemplateRef; - - /** - * @ignore - */ - @ViewChild('layout', { static: false, read: ViewContainerRef }) layoutContainer: ViewContainerRef; - /** - * @ignore - */ - @ContentChild(StreamDirective) - set externalStream(externalStream: StreamDirective) { - // This directive will has value only when STREAM component tagget with '*ovStream' directive - // is inside of the layout component tagged with '*ovLayout' directive - if (externalStream) { - this.streamTemplate = externalStream.template; - } - } - - localParticipant: ParticipantAbstractModel; - remoteParticipants: ParticipantAbstractModel[] = []; - /** - * @ignore - */ - captionsEnabled = true; - - private localParticipantSubs: Subscription; - private remoteParticipantsSubs: Subscription; - private captionsSubs: Subscription; - - /** - * @ignore - */ - constructor(protected layoutService: LayoutService, protected participantService: ParticipantService, private cd: ChangeDetectorRef) {} - - ngOnInit(): void { - this.subscribeToParticipants(); - this.subscribeToCaptions(); - } - - ngAfterViewInit() { - this.layoutService.initialize(this.layoutContainer.element.nativeElement); - } - - ngOnDestroy() { - this.localParticipant = null; - this.remoteParticipants = []; - if (this.localParticipantSubs) this.localParticipantSubs.unsubscribe(); - if (this.remoteParticipantsSubs) this.remoteParticipantsSubs.unsubscribe(); - if (this.captionsSubs) this.captionsSubs.unsubscribe(); - this.layoutService.clear(); - } - - private subscribeToCaptions() { - this.captionsSubs = this.layoutService.captionsTogglingObs.subscribe((value: boolean) => { - this.captionsEnabled = value; - this.cd.markForCheck(); - this.layoutService.update(); - }); - } - - private subscribeToParticipants() { - this.localParticipantSubs = this.participantService.localParticipantObs.subscribe((p) => { - this.localParticipant = p; - this.layoutService.update(); - this.cd.markForCheck(); - }); - - this.remoteParticipantsSubs = this.participantService.remoteParticipantsObs.subscribe((participants) => { - this.remoteParticipants = participants; - this.layoutService.update(); - this.cd.markForCheck(); - }); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/activities-panel.component.css b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/activities-panel.component.css deleted file mode 100644 index fbf42f8a..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/activities-panel.component.css +++ /dev/null @@ -1,86 +0,0 @@ -.activities-body-container { - display: block !important; - overflow-y: auto; - overflow-x: hidden; - padding: 10px; -} - -.activity-icon { - display: inherit; - background-color: var(--ov-light-color); - border-radius: var(--ov-panel-radius); - margin: auto; - padding: 10px; -} - -::ng-deep .mat-mdc-list-item-icon { - margin: auto !important; -} - - -.activity-title, -.activity-subtitle { - margin: 0; - color: var(--ov-panel-text-color); -} - -.activity-title { - font-weight: bold !important; -} - -.activity-subtitle { - font-style: italic; - font-size: 11px !important; -} - -.activity-action-buttons { - font-weight: 600; - position: absolute; - right: 15px; - top: 0px; -} - -::ng-deep .mat-content { - display: block !important; -} - -/* .activity-icon mat-icon { - margin: auto; -} */ - -/* TODO(mdc-migration): The following rule targets internal classes of list that may no longer apply for the MDC version. */ -::ng-deep .mdc-list-item__content { - padding-left: 10px !important; - align-self: center !important; -} - -::ng-deep .mat-expansion-panel-header { - padding: 0px 5px !important; - height: 65px !important; -} - -::ng-deep .mat-mdc-list-base .mat-mdc-list-item .mat-list-item-content, -.mat-list-base .mat-list-option .mat-list-item-content { - padding: 0px !important; -} - -::ng-deep mat-expansion-panel .mat-expansion-panel-body { - padding: 0px !important; - min-height: 400px; -} -::ng-deep .mat-expansion-panel-header-description { - flex-grow: 0 !important; -} - -::ng-deep .mat-expansion-panel { - box-shadow: none !important; -} - -::ng-deep .no-body .mat-expansion-panel-content { - display: none !important; -} - -::ng-deep .mdc-list-item--with-leading-icon.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end::before { - max-height: 24px; - height: 24px; -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/activities-panel.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/activities-panel.component.ts deleted file mode 100644 index d28862d5..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/activities-panel.component.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, OnInit, Output } from '@angular/core'; -import { Subscription } from 'rxjs'; -import { PanelEvent, PanelType } from '../../../models/panel.model'; -import { OpenViduAngularConfigService } from '../../../services/config/openvidu-angular.config.service'; -import { PanelService } from '../../../services/panel/panel.service'; - -@Component({ - selector: 'ov-activities-panel', - templateUrl: './activities-panel.component.html', - styleUrls: ['../panel.component.css', './activities-panel.component.css'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class ActivitiesPanelComponent implements OnInit { - /** - * Provides event notifications that fire when start recording button has been clicked. - * The recording should be started using the OpenVidu REST API. - */ - @Output() onStartRecordingClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when stop recording button has been clicked. - * The recording should be stopped using the OpenVidu REST API. - */ - @Output() onStopRecordingClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when delete recording button has been clicked. - * The recording should be deleted using the OpenVidu REST API. - */ - @Output() onDeleteRecordingClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when a participant needs update the recordings information - * (usually when recording is stopped by the session moderator or recording panel is opened). - * The recordings should be updated using the REST API. - */ - @Output() onForceRecordingUpdate: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when start broadcasting button has been clicked. - * The broadcasting should be started using the REST API. - */ - @Output() onStartBroadcastingClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when stop broadcasting button has been clicked. - * The broadcasting should be stopped using the REST API. - */ - @Output() onStopBroadcastingClicked: EventEmitter = new EventEmitter(); - - /** - * @internal - */ - expandedPanel: string = ''; - /** - * @internal - */ - showRecordingActivity: boolean = true; - showBroadcastingActivity: boolean = true; - private panelSubscription: Subscription; - private recordingActivitySub: Subscription; - private broadcastingActivitySub: Subscription; - - /** - * @internal - */ - constructor(private panelService: PanelService, private libService: OpenViduAngularConfigService, private cd: ChangeDetectorRef) {} - - /** - * @internal - */ - ngOnInit(): void { - this.subscribeToPanelToggling(); - this.subscribeToActivitiesPanelDirective(); - } - - /** - * @internal - */ - ngOnDestroy() { - if (this.panelSubscription) this.panelSubscription.unsubscribe(); - if (this.recordingActivitySub) this.recordingActivitySub.unsubscribe(); - if (this.broadcastingActivitySub) this.broadcastingActivitySub.unsubscribe(); - } - - /** - * @internal - */ - close() { - this.panelService.togglePanel(PanelType.ACTIVITIES); - } - - /** - * @internal - */ - _onStartRecordingClicked() { - this.onStartRecordingClicked.emit(); - } - - /** - * @internal - */ - _onStopRecordingClicked() { - this.onStopRecordingClicked.emit(); - } - - /** - * @internal - */ - _onDeleteRecordingClicked(recordingId: string) { - this.onDeleteRecordingClicked.emit(recordingId); - } - - _onStartBroadcastingClicked(broadcastUrl: string) { - this.onStartBroadcastingClicked.emit(broadcastUrl); - } - - _onStopBroadcastingClicked() { - this.onStopBroadcastingClicked.emit(); - } - - _onForceRecordingUpdate() { - this.onForceRecordingUpdate.emit(); - } - - private subscribeToPanelToggling() { - this.panelSubscription = this.panelService.panelOpenedObs.subscribe((ev: PanelEvent) => { - if (ev.type === PanelType.ACTIVITIES && !!ev.expand) { - this.expandedPanel = ev.expand; - } - }); - } - - private subscribeToActivitiesPanelDirective() { - this.recordingActivitySub = this.libService.recordingActivity.subscribe((value: boolean) => { - this.showRecordingActivity = value; - this.cd.markForCheck(); - }); - - this.broadcastingActivitySub = this.libService.broadcastingActivity.subscribe((value: boolean) => { - this.showBroadcastingActivity = value; - this.cd.markForCheck(); - }); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.css b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.css deleted file mode 100644 index 8a23513c..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.css +++ /dev/null @@ -1,149 +0,0 @@ -#recording-status { - color: var(--ov-text-color); - display: inline; - padding: 3px; - font-size: 11px; - border-radius: var(--ov-panel-radius); -} - -.recording-title { - margin: 0px; -} - -.time-container { - padding: 2px; -} -.recording-icon { - font-size: 32px !important; - width: 32px !important; - margin: 0 !important; - align-self: center !important; - display: flex; - flex-wrap: wrap; - height: auto; - align-content: center; - color: var(--ov-panel-text-color) !important; -} - -.recording-duration { - background-color: var(--ov-light-color); - padding: 4px 8px; - border-radius: var(--ov-panel-radius); - font-weight: 500; -} - -.recording-duration mat-icon { - font-size: 18px; - width: 18px; - height: 18px; -} - -.started { - background-color: #3b7430 !important; - color: var(--ov-text-color); -} - -.activity-icon.started, -.failed { - background-color: var(--ov-warn-color) !important; - color: var(--ov-text-color); -} -.stopped { - background-color: var(--ov-light-color); - color: var(--ov-panel-text-color) !important; -} - -.pending { - background-color: #ffd79b !important; - color: var(--ov-panel-text-color) !important; -} - -.panel-body-container { - padding: 10px; -} - -.panel-body-container > .content { - align-items: stretch; - justify-content: center; - display: flex; - flex-direction: column; - box-flex: 1; - flex-grow: 1; - text-align: center; -} - -.recording-error { - color: var(--ov-warn-color); - font-weight: 600; -} -.recording-name { - font-size: 16px; - font-weight: bold; -} - -.recording-date { - font-size: 12px !important; - font-style: italic; -} - -.not-allowed-message { - margin-top: 10px; - font-weight: bold; -} - -.recording-action-buttons { - margin-top: 20px; - margin-bottom: 20px; -} - -#recording-action-buttons { - position: absolute; - bottom: 10px; - right: 0; -} - -#start-recording-btn { - width: 100%; - background-color: var(--ov-tertiary-color); - color: var(--ov-text-color); -} - -#stop-recording-btn { - width: 100%; - background-color: var(--ov-warn-color); - color: var(--ov-text-color); -} - -.delete-recording-btn { - color: var(--ov-warn-color); -} - -#reset-recording-status-btn { - width: 100%; - background-color: var(--ov-light-color); -} - -mat-expansion-panel { - margin: 0px 0px 5px 0px; -} - -::ng-deep .mdc-list { - padding: 0px; -} - -::ng-deep .mdc-list-item__secondary-text { - display: flex; -} - -::ng-deep .mat-mdc-list-item-unscoped-content { - height: 0px !important; -} - -.blink { - animation: blinker 1.5s linear infinite !important; -} -@keyframes blinker { - 50% { - opacity: 0.4; - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.ts deleted file mode 100644 index 39e2fa17..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { Subscription } from 'rxjs'; -import { RecordingInfo, RecordingStatus } from '../../../../models/recording.model'; -import { ActionService } from '../../../../services/action/action.service'; -import { OpenViduAngularConfigService } from '../../../../services/config/openvidu-angular.config.service'; -import { ParticipantService } from '../../../../services/participant/participant.service'; -import { RecordingService } from '../../../../services/recording/recording.service'; -import { OpenViduService } from '../../../../services/openvidu/openvidu.service'; -import { Signal } from '../../../../models/signal.model'; - -@Component({ - selector: 'ov-recording-activity', - templateUrl: './recording-activity.component.html', - styleUrls: ['./recording-activity.component.css', '../activities-panel.component.css'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class RecordingActivityComponent implements OnInit { - /** - * @internal - */ - @Input() expanded: boolean; - - /** - * Provides event notifications that fire when start recording button has been clicked. - * The recording should be started using the REST API. - */ - @Output() onStartRecordingClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when stop recording button has been clicked. - * The recording should be stopped using the REST API. - */ - @Output() onStopRecordingClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when delete recording button has been clicked. - * The recording should be deleted using the REST API. - */ - @Output() onDeleteRecordingClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when a participant needs update the recordings information - * (usually when recording is stopped by the session moderator or recording panel is opened). - * The recordings should be updated using the REST API. - */ - @Output() onForceRecordingUpdate: EventEmitter = new EventEmitter(); - - /** - * @internal - */ - recordingStatus: RecordingStatus = RecordingStatus.STOPPED; - /** - * @internal - */ - oldRecordingStatus: RecordingStatus; - /** - * @internal - */ - opened: boolean = false; - - /** - * @internal - */ - recStatusEnum = RecordingStatus; - - /** - * @internal - */ - isSessionCreator = false; - - /** - * @internal - */ - recordingAlive: boolean = false; - /** - * @internal - */ - recordingsList: RecordingInfo[] = []; - - /** - * @internal - */ - recordingError: any; - - private recordingStatusSubscription: Subscription; - private recordingListSubscription: Subscription; - private recordingErrorSub: Subscription; - private forceRecordingUpdateSub: Subscription; - - /** - * @internal - */ - constructor( - private recordingService: RecordingService, - private participantService: ParticipantService, - private libService: OpenViduAngularConfigService, - private actionService: ActionService, - private openviduService: OpenViduService, - private cd: ChangeDetectorRef - ) {} - - /** - * @internal - */ - ngOnInit(): void { - this.subscribeToRecordingStatus(); - this.subscribeToRecordingActivityDirective(); - this.subscribeToForceRecordingUpdate(); - this.isSessionCreator = this.participantService.amIModerator(); - } - - /** - * @internal - */ - ngOnDestroy() { - if (this.recordingStatusSubscription) this.recordingStatusSubscription.unsubscribe(); - if (this.recordingListSubscription) this.recordingListSubscription.unsubscribe(); - if (this.recordingErrorSub) this.recordingErrorSub.unsubscribe(); - if (this.forceRecordingUpdateSub) this.forceRecordingUpdateSub.unsubscribe(); - } - - /** - * @internal - */ - panelOpened() { - this.opened = true; - } - - /** - * @internal - */ - panelClosed() { - this.opened = false; - } - - /** - * @internal - */ - resetStatus() { - let status: RecordingStatus = this.oldRecordingStatus; - if (this.oldRecordingStatus === RecordingStatus.STARTING) { - status = RecordingStatus.STOPPED; - } else if (this.oldRecordingStatus === RecordingStatus.STOPPING) { - status = RecordingStatus.STARTED; - } - this.recordingService.updateStatus(status); - } - - /** - * @internal - */ - startRecording() { - this.onStartRecordingClicked.emit(); - this.recordingService.updateStatus(RecordingStatus.STARTING); - } - - /** - * @internal - */ - stopRecording() { - this.onStopRecordingClicked.emit(); - this.recordingService.updateStatus(RecordingStatus.STOPPING); - } - - /** - * @internal - */ - - deleteRecording(id: string) { - const succsessCallback = async () => { - this.onDeleteRecordingClicked.emit(id); - // Sending signal to all participants with the aim of updating their recordings list - await this.openviduService.sendSignal(Signal.RECORDING_DELETED, this.openviduService.getRemoteConnections()); - }; - this.actionService.openDeleteRecordingDialog(succsessCallback.bind(this)); - } - - /** - * @internal - */ - download(recording: RecordingInfo) { - this.recordingService.downloadRecording(recording); - } - - /** - * @internal - */ - play(recording: RecordingInfo) { - this.recordingService.playRecording(recording); - } - - private subscribeToRecordingStatus() { - this.recordingStatusSubscription = this.recordingService.recordingStatusObs.subscribe( - (ev?: { info: RecordingInfo; time?: Date }) => { - if (ev?.info) { - if (this.recordingStatus !== RecordingStatus.FAILED) { - this.oldRecordingStatus = this.recordingStatus; - if (ev.info.status === RecordingStatus.STOPPED && !this.isSessionCreator) { - // Force update recording list when recording is stopped by moderator - this.onForceRecordingUpdate.emit(); - } - } - this.recordingStatus = ev.info.status; - this.recordingAlive = ev.info.status === RecordingStatus.STARTED; - this.cd.markForCheck(); - } - } - ); - } - - private subscribeToRecordingActivityDirective() { - this.recordingListSubscription = this.libService.recordingsListObs.subscribe((recordingList: RecordingInfo[]) => { - this.recordingsList = recordingList; - // si envio aqui el signal, estos eventos serian infinitos - // this.openviduService.sendSignal(Signal.RECORDING_DELETED, this.openviduService.getRemoteConnections()); - this.cd.markForCheck(); - }); - - this.recordingErrorSub = this.libService.recordingErrorObs.subscribe((error: any) => { - if (error) { - this.recordingService.updateStatus(RecordingStatus.FAILED); - this.recordingError = error.error?.message || error.message || error; - } - }); - } - - private subscribeToForceRecordingUpdate() { - this.forceRecordingUpdateSub = this.recordingService.forceUpdateRecordingsObs.subscribe(() => { - this.onForceRecordingUpdate.emit(); - }); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.ts deleted file mode 100644 index dac4f97d..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; -import { Subscription } from 'rxjs'; -import { BackgroundEffect, EffectType } from '../../../models/background-effect.model'; -import { PanelType } from '../../../models/panel.model'; -import { PanelService } from '../../../services/panel/panel.service'; -import { VirtualBackgroundService } from '../../../services/virtual-background/virtual-background.service'; - -/** - * @internal - */ -@Component({ - selector: 'ov-background-effects-panel', - templateUrl: './background-effects-panel.component.html', - styleUrls: ['../panel.component.css','./background-effects-panel.component.css'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class BackgroundEffectsPanelComponent implements OnInit { - - backgroundSelectedId: string; - effectType = EffectType; - backgroundImages: BackgroundEffect[] = []; - noEffectAndBlurredBackground: BackgroundEffect[] = []; - private backgrounds: BackgroundEffect[]; - private backgroundSubs: Subscription; - - /** - * @internal - * @param panelService - * @param backgroundService - * @param cd - */ - constructor(private panelService: PanelService, private backgroundService: VirtualBackgroundService, private cd: ChangeDetectorRef) { } - - ngOnInit(): void { - this.subscribeToBackgroundSelected(); - this.backgrounds = this.backgroundService.getBackgrounds(); - this.noEffectAndBlurredBackground = this.backgrounds.filter(f => f.type === EffectType.BLUR || f.type === EffectType.NONE); - this.backgroundImages = this.backgrounds.filter(f => f.type === EffectType.IMAGE); - } - - ngOnDestroy() { - if (this.backgroundSubs) this.backgroundSubs.unsubscribe(); - } - subscribeToBackgroundSelected() { - this.backgroundSubs = this.backgroundService.backgroundSelectedObs.subscribe((id) => { - this.backgroundSelectedId = id; - this.cd.markForCheck(); - }); - } - - close() { - this.panelService.togglePanel(PanelType.BACKGROUND_EFFECTS); - } - - async applyBackground(effect: BackgroundEffect) { - if (effect.type === EffectType.NONE) { - await this.removeBackground(); - } else { - await this.backgroundService.applyBackground(effect); - } - } - - async removeBackground() { - await this.backgroundService.removeBackground(); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/panel.component.spec.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/panel.component.spec.ts deleted file mode 100644 index b0f6a9d7..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/panel.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { PanelComponent } from './panel.component'; - -describe('PanelComponent', () => { - let component: PanelComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ PanelComponent ] - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(PanelComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.css b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.css deleted file mode 100644 index 0e732a54..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.css +++ /dev/null @@ -1,60 +0,0 @@ -.participant-avatar { - display: inherit; - border-radius: var(--ov-panel-radius); - margin: auto !important; - padding: 10px; - color: var(--ov-panel-text-color); -} - -.participant-subtitle { - font-style: italic; - font-size: 11px !important; - margin: 0; -} - -.participant-nickname { - font-weight: bold !important; - margin: 0; - color: var(--ov-panel-text-color); -} - -.participant-action-buttons { - display: flex; -} - -::ng-deep .participant-action-buttons > *:not(#mute-btn) { - display: contents; -} - -::ng-deep .participant-action-buttons > *:not(#mute-btn) > * { - margin: auto; -} - - -::ng-deep .mat-mdc-list-item { - height: max-content !important; - padding-bottom: 10px !important; -} - -mat-list { - padding: 3px; -} - -::ng-deep .mdc-list-item__content { - padding-left: 10px !important; - align-self: center !important; -} - -::ng-deep .mat-mdc-list-base { - --mdc-list-list-item-hover-label-text-color: unset; - --mdc-list-list-item-hover-leading-icon-color: unset; -} - -#mute-btn { - border-radius: var(--ov-buttons-radius); -} - -.warn-btn { - /* background-color: var(--ov-warn-color) !important; */ - color: var(--ov-warn-color); -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.ts deleted file mode 100644 index 3421b963..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core'; -import { Subscription } from 'rxjs'; -import { ParticipantPanelItemElementsDirective } from '../../../../directives/template/openvidu-angular.directive'; -import { ParticipantAbstractModel } from '../../../../models/participant.model'; -import { OpenViduAngularConfigService } from '../../../../services/config/openvidu-angular.config.service'; -import { ParticipantService } from '../../../../services/participant/participant.service'; - -/** - * - * The **ParticipantPanelItemComponent** is hosted inside of the {@link ParticipantsPanelComponent}. - * It is in charge of displaying the participants information inside of the ParticipansPanelComponent. - * - *
- *
- *

API Directives

- * - * This component allows us to show or hide certain HTML elements with the following {@link https://angular.io/guide/attribute-directives Angular attribute directives} - * with the aim of fully customizing the ToolbarComponent. - * - * | **Name** | **Type** | **Reference** | - * | :----------------------------: | :-------: | :---------------------------------------------: | - * | **muteButton** | `boolean` | {@link ParticipantPanelItemMuteButtonDirective} | - * - * - * - *
- *
- * - *

OpenVidu Angular Directives

- * - * The ParticipantPanelItemComponent can be replaced with a custom component. It provides us the following {@link https://angular.io/guide/structural-directives Angular structural directives} - * for doing this. - * - * | **Directive** | **Reference** | - * |:----------------------------------:|:---------------------------------------------:| - * | ***ovParticipantPanelItem** | {@link ParticipantPanelItemDirective} | - * - *
- * - * It is also providing us a way to **add additional buttons** to the default participant panel item. - * It will recognise the following directive in a child element. - * - * | **Directive** | **Reference** | - * |:----------------------------------:|:---------------------------------------------:| - * | ***ovParticipantPanelItemElements** | {@link ParticipantPanelItemElementsDirective} | - * - * - *
- *
- */ - -@Component({ - selector: 'ov-participant-panel-item', - templateUrl: './participant-panel-item.component.html', - styleUrls: ['./participant-panel-item.component.css'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class ParticipantPanelItemComponent implements OnInit, OnDestroy { - /** - * @ignore - */ - @ContentChild('participantPanelItemElements', { read: TemplateRef }) participantPanelItemElementsTemplate: TemplateRef; - - /** - * @ignore - */ - showMuteButton: boolean = true; - private muteButtonSub: Subscription; - - /** - * @ignore - */ - @ContentChild(ParticipantPanelItemElementsDirective) - set externalItemElements(externalItemElements: ParticipantPanelItemElementsDirective) { - // This directive will has value only when ITEM ELEMENTS component tagget with '*ovParticipantPanelItemElements' directive - // is inside of the P PANEL ITEM component tagged with '*ovParticipantPanelItem' directive - if (externalItemElements) { - this.participantPanelItemElementsTemplate = externalItemElements.template; - } - } - - @Input() - set participant(participant: ParticipantAbstractModel) { - this._participant = participant; - } - - /** - * @ignore - */ - _participant: ParticipantAbstractModel; - - /** - * @ignore - */ - constructor(private libService: OpenViduAngularConfigService, protected participantService: ParticipantService, private cd: ChangeDetectorRef) {} - - ngOnInit(): void { - this.subscribeToParticipantPanelItemDirectives(); - } - ngOnDestroy(): void { - if (this.muteButtonSub) this.muteButtonSub.unsubscribe(); - } - - /** - * @ignore - */ - toggleMuteForcibly() { - this.participantService.setRemoteMutedForcibly(this._participant.id, !this._participant.isMutedForcibly); - } - - private subscribeToParticipantPanelItemDirectives() { - this.muteButtonSub = this.libService.participantItemMuteButton.subscribe((value: boolean) => { - this.showMuteButton = value; - this.cd.markForCheck(); - }); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/participants-panel/participants-panel/participants-panel.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/participants-panel/participants-panel/participants-panel.component.ts deleted file mode 100644 index 5e593a79..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/participants-panel/participants-panel/participants-panel.component.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; -import { ParticipantAbstractModel } from '../../../../models/participant.model'; -import { ParticipantService } from '../../../../services/participant/participant.service'; -import { PanelService } from '../../../..//services/panel/panel.service'; -import { ParticipantPanelItemDirective } from '../../../../directives/template/openvidu-angular.directive'; -import { Subscription } from 'rxjs'; - -/** - * - * The **ParticipantsPanelComponent** is hosted inside of the {@link PanelComponent}. - * It is in charge of displaying the participants connected to the session. - * This component is composed by the {@link ParticipantPanelItemComponent}. - * - *
- *
- * - *

OpenVidu Angular Directives

- * - * The ParticipantsPanelComponent can be replaced with a custom component. It provides us the following {@link https://angular.io/guide/structural-directives Angular structural directives} - * for doing this. - * - * | **Directive** | **Reference** | - * |:----------------------------------:|:---------------------------------------------:| - * | ***ovParticipantsPanel** | {@link ParticipantsPanelDirective} | - * - *
- * - * As the ParticipantsPanelComponent is composed by ParticipantPanelItemComponent, it is also providing us a way to **replace the participant item** with a custom one. - * It will recognise the following directive in a child element. - * - * | **Directive** | **Reference** | - * |:----------------------------------:|:---------------------------------------------:| - * | ***ovParticipantPanelItem** | {@link ParticipantPanelItemDirective} | - * - * - *
- *
- */ -@Component({ - selector: 'ov-participants-panel', - templateUrl: './participants-panel.component.html', - styleUrls: ['../../panel.component.css','./participants-panel.component.css'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class ParticipantsPanelComponent implements OnInit, OnDestroy, AfterViewInit { - localParticipant: any; - remoteParticipants: ParticipantAbstractModel[] = []; - - /** - * @ignore - */ - @ViewChild('defaultParticipantPanelItem', { static: false, read: TemplateRef }) defaultParticipantPanelItemTemplate: TemplateRef; - - /** - * @ignore - */ - @ContentChild('participantPanelItem', { read: TemplateRef }) participantPanelItemTemplate: TemplateRef; - - @ContentChild(ParticipantPanelItemDirective) - set externalParticipantPanelItem(externalParticipantPanelItem: ParticipantPanelItemDirective) { - // This directive will has value only when PARTICIPANT PANEL ITEM component tagged with '*ovParticipantPanelItem' - // is inside of the PARTICIPANTS PANEL component tagged with '*ovParticipantsPanel' - if (externalParticipantPanelItem) { - this.participantPanelItemTemplate = externalParticipantPanelItem.template; - } - } - - private localParticipantSubs: Subscription; - private remoteParticipantsSubs: Subscription; - - /** - * @ignore - */ - constructor( - protected participantService: ParticipantService, - protected panelService: PanelService, - private cd: ChangeDetectorRef - ) {} - - ngOnInit(): void { - this.localParticipantSubs = this.participantService.localParticipantObs.subscribe((p: ParticipantAbstractModel) => { - this.localParticipant = p; - // Mark for re-rendering using an impure pipe 'streamsTypesEnabled' - this.cd.markForCheck(); - }); - - this.remoteParticipantsSubs = this.participantService.remoteParticipantsObs.subscribe((p: ParticipantAbstractModel[]) => { - // Workaround which forc the objects references update - // After one entirely day trying to make it works, this is the only way - const remoteParticipantsAux = []; - p.forEach((par) => { - remoteParticipantsAux.push(Object.create(par)); - }); - this.remoteParticipants = remoteParticipantsAux; - // Mark for re-rendering using an impure pipe 'streamsTypesEnabled' - this.cd.markForCheck(); - }); - } - - ngOnDestroy() { - if (this.localParticipantSubs) this.localParticipantSubs.unsubscribe(); - if (this.remoteParticipantsSubs) this.remoteParticipantsSubs.unsubscribe; - } - - ngAfterViewInit(){ - if(!this.participantPanelItemTemplate) { - // the user has override the default participants panel but not the 'participant-panel-item' - // so the default component must be injected - this.participantPanelItemTemplate = this.defaultParticipantPanelItemTemplate - this.cd.detectChanges(); - } - } - - close() { - this.panelService.closePanel(); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/settings-panel/settings-panel.component.css b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/settings-panel/settings-panel.component.css deleted file mode 100644 index e5f14616..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/settings-panel/settings-panel.component.css +++ /dev/null @@ -1,54 +0,0 @@ -#settings-container { - display: flex; -} - -.settings-container { - display: flex; - padding: 10px; - flex: 1; - width: auto; - justify-content: space-between; - align-items: stretch; -} - -.item-menu { - padding-right: 5px; - border-right: 1px solid var(--ov-secondary-color); - width: 170px; -} -.item-menu.mobile { - width: 50px !important; -} - -.item-content { - padding: 16px; - flex-grow: 1; - width: min-content; -} - -.lang-container button { - width: 100%; -} - -/* .option { - border-radius: var(--ov-panel-radius); -} */ -mat-list-option[aria-selected='true'] { - background: var(--ov-tertiary-color) !important; - border-radius: var(--ov-panel-radius); -} - -::ng-deep mat-list-option[aria-selected='true'] .mat-mdc-list-item-unscoped-content, ::ng-deep mat-list-option[aria-selected='true'] mat-icon { - color: var(--ov-panel-background) !important; -} - -mat-list-option[aria-selected='false'] mat-icon { - color: var(--ov-panel-text-color) !important; -} - -::ng-deep .mdc-list-item--with-leading-icon .mdc-list-item__start { - margin-right: 15px !important; -} -.mat-mdc-list-base { - --mdc-list-list-item-focus-state-layer-color: transparent !important; -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/pre-join/pre-join.component.css b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/pre-join/pre-join.component.css deleted file mode 100644 index 9f851e1d..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/pre-join/pre-join.component.css +++ /dev/null @@ -1,165 +0,0 @@ -.container { - height: calc(100% - 64px); - padding: 30px 60px; - background-color: var(--ov-light-color); -} - -.prejoin-toolbar { - max-height: 40px; - height: 40px; -} -#toolbar { - background-color: var(--ov-light-color); - height: 100%; -} -#toolbar ::ng-deep .lang-button { - height: 25px !important; - font-size: 14px !important; -} -.spacer { - flex: 1 1 auto; -} - -#branding-logo { - border-radius: var(--ov-panel-radius); - max-width: 35px; - max-height: 35px; - height: -webkit-fill-available; - height: -moz-available; - padding: 10px; - margin-right: 10px; -} - -#layout-container { - display: block !important; -} - -h4 { - margin-bottom: 1px; - font-weight: bold; -} -hr { - margin: 0; -} - -#prejoin-container { - height: calc(100% - 40px); -} -#prejoin-container ::ng-deep .sidenav-container { - padding-top: 0px !important; -} - -#prejoin-container ::ng-deep #background-effects-container { - margin: 0px !important; - max-height: 100% !important; - height: 100% !important; -} - -#prejoin-container ::ng-deep .mat-drawer-container, -#prejoin-container ::ng-deep .sidenav-menu, -#prejoin-container ::ng-deep #session-container { - background-color: transparent !important; -} - -#prejoin-container ::ng-deep .sidenav-menu { - width: 320px; -} - -#prejoin-container ::ng-deep .layout { - min-width: 0px !important; -} - -#prejoin-container ::ng-deep .OT_root { - padding: 0px !important; -} - -#background-effects-btn { - position: absolute; - z-index: 2; - background-color: var(--ov-secondary-color); - bottom: 5px; - right: 5px; -} - -.media-panel { - background-color: var(--ov-light-color); -} -.media-panel-container { - width: 100%; - padding: 20px; - padding-right: 0px; -} - -.nickname-container { - /* padding: 10px; */ - display: block !important; - margin-bottom: 0px !important; -} - -/* TODO(mdc-migration): The following rule targets internal classes of form-field that may no longer apply for the MDC version. */ -.mat-form-field-appearance-fill .mat-form-field-flex { - /* background-color: var(--ov-text-color); */ - border-radius: var(--ov-video-radius); -} - -.buttons-container { - border-radius: 5px; - padding: 10px 0px; - height: 100px; - display: block !important; -} - -.join-btn-container { - width: inherit; - text-align: center; -} - -#join-button { - width: 100%; - font-weight: bold; - color: var(--ov-text-color); - background-color: var(--ov-tertiary-color); - border-radius: var(--ov-video-radius); -} - - @media only screen and (max-width: 480px) { - .container, - .media-panel-container, - .buttons-container { - padding: 0; - } - .nickname-container, - .buttons-container, - .join-btn-container { - width: 90% !important; - margin: auto; - } - .join-btn-container { - padding: 0px 10px; - } - .media-panel { - align-items: flex-start !important; - } -} - -@media only screen and (min-width: 480px) and (max-width: 959px) { - /* .container, */ - .media-panel-container, - .buttons-container { - padding: 0; - } - - .nickname-container, - .buttons-container, - .join-btn-container { - width: 80% !important; - min-width: 80% !important; - margin: auto; - } - - .buttons-container, - .media-panel-container { - padding-top: 0px; - max-width: 600px; - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/pre-join/pre-join.component.html b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/pre-join/pre-join.component.html deleted file mode 100644 index 9b94b66a..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/pre-join/pre-join.component.html +++ /dev/null @@ -1,72 +0,0 @@ -
- - - - - - - -
- -
-
- - - - - - - - - - - - - - - - - - -
-
-
-
-
-

{{ 'PREJOIN.NICKNAME_SECTION' | translate }}

-
- -
- -
-

{{ 'PREJOIN.DEVICE_SECTION' | translate }}

-
- - - - - - -
- -
- -
-
-
-
-
diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/pre-join/pre-join.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/pre-join/pre-join.component.ts deleted file mode 100644 index 637e8bc8..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/pre-join/pre-join.component.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { Component, EventEmitter, HostListener, OnDestroy, OnInit, Output } from '@angular/core'; - -import { Subscription } from 'rxjs'; -import { ILogger } from '../../models/logger.model'; -import { PanelType } from '../../models/panel.model'; -import { ParticipantAbstractModel } from '../../models/participant.model'; -import { ActionService } from '../../services/action/action.service'; -import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.service'; -import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; -import { LayoutService } from '../../services/layout/layout.service'; -import { LoggerService } from '../../services/logger/logger.service'; -import { OpenViduService } from '../../services/openvidu/openvidu.service'; -import { PanelService } from '../../services/panel/panel.service'; -import { ParticipantService } from '../../services/participant/participant.service'; -import { TranslateService } from '../../services/translate/translate.service'; - -/** - * @internal - */ -@Component({ - selector: 'ov-pre-join', - templateUrl: './pre-join.component.html', - styleUrls: ['./pre-join.component.css'] -}) -export class PreJoinComponent implements OnInit, OnDestroy { - @Output() onJoinButtonClicked = new EventEmitter(); - - localParticipant: ParticipantAbstractModel; - windowSize: number; - isLoading = true; - nickname: string; - /** - * @ignore - */ - showBackgroundEffectsButton: boolean = true; - /** - * @ignore - */ - isMinimal: boolean = false; - showLogo: boolean = true; - - private log: ILogger; - private localParticipantSubscription: Subscription; - private screenShareStateSubscription: Subscription; - private minimalSub: Subscription; - private displayLogoSub: Subscription; - private backgroundEffectsButtonSub: Subscription; - - @HostListener('window:resize') - sizeChange() { - this.windowSize = window.innerWidth; - this.layoutService.update(); - } - - constructor( - private layoutService: LayoutService, - private loggerSrv: LoggerService, - private participantService: ParticipantService, - protected panelService: PanelService, - private libService: OpenViduAngularConfigService, - protected cdkSrv: CdkOverlayService, - private openviduService: OpenViduService, - private translateService: TranslateService, - private actionService: ActionService - ) { - this.log = this.loggerSrv.get('PreJoinComponent'); - } - - ngOnInit() { - this.subscribeToPrejoinDirectives(); - this.subscribeToLocalParticipantEvents(); - - this.windowSize = window.innerWidth; - this.isLoading = false; - } - - async ngOnDestroy() { - this.cdkSrv.setSelector('body'); - if (this.localParticipantSubscription) this.localParticipantSubscription.unsubscribe(); - if (this.screenShareStateSubscription) this.screenShareStateSubscription.unsubscribe(); - if (this.backgroundEffectsButtonSub) this.backgroundEffectsButtonSub.unsubscribe(); - if (this.minimalSub) this.minimalSub.unsubscribe(); - this.panelService.closePanel(); - } - - onDeviceSelectorClicked() { - // Some devices as iPhone do not show the menu panels correctly - // Updating the container where the panel is added fix the problem. - this.cdkSrv.setSelector('#prejoin-container'); - } - - onVideoMutedClicked(hasVideo: boolean) { - if (!hasVideo) { - this.panelService.closePanel(); - } - } - - joinSession() { - this.onJoinButtonClicked.emit(); - this.panelService.closePanel(); - } - - toggleBackgroundEffects() { - if (this.openviduService.isOpenViduPro()) { - this.panelService.togglePanel(PanelType.BACKGROUND_EFFECTS); - } else { - this.actionService.openProFeatureDialog( - this.translateService.translate('PANEL.BACKGROUND.TITLE'), - this.translateService.translate('PANEL.PRO_FEATURE') - ); - } - } - - private subscribeToLocalParticipantEvents() { - this.localParticipantSubscription = this.participantService.localParticipantObs.subscribe((p) => { - this.localParticipant = p; - this.nickname = this.localParticipant.getNickname(); - }); - } - - private subscribeToPrejoinDirectives() { - this.minimalSub = this.libService.minimalObs.subscribe((value: boolean) => { - this.isMinimal = value; - // this.cd.markForCheck(); - }); - this.displayLogoSub = this.libService.displayLogoObs.subscribe((value: boolean) => { - this.showLogo = value; - // this.cd.markForCheck(); - }); - this.backgroundEffectsButtonSub = this.libService.backgroundEffectsButton.subscribe((value: boolean) => { - this.showBackgroundEffectsButton = value; - // this.cd.markForCheck(); - }); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/session/session.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/session/session.component.ts deleted file mode 100644 index 5b8501e6..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/session/session.component.ts +++ /dev/null @@ -1,466 +0,0 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ContentChild, - ElementRef, - EventEmitter, - HostListener, - Input, - OnDestroy, - OnInit, - Output, - TemplateRef, - ViewChild -} from '@angular/core'; -import { - ConnectionEvent, - ExceptionEvent, - ExceptionEventName, - RecordingEvent, - Session, - SessionDisconnectedEvent, - StreamEvent, - StreamPropertyChangedEvent, - Subscriber -} from 'openvidu-browser-v2compatibility'; - -import { ILogger } from '../../models/logger.model'; -import { VideoType } from '../../models/video-type.model'; - -import { animate, style, transition, trigger } from '@angular/animations'; -import { MatDrawerContainer, MatSidenav } from '@angular/material/sidenav'; -import { skip, Subscription } from 'rxjs'; -import { SidenavMode } from '../../models/layout.model'; -import { PanelEvent, PanelType } from '../../models/panel.model'; -import { Signal } from '../../models/signal.model'; -import { ActionService } from '../../services/action/action.service'; -import { BroadcastingService } from '../../services/broadcasting/broadcasting.service'; -import { CaptionService } from '../../services/caption/caption.service'; -import { ChatService } from '../../services/chat/chat.service'; -import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; -import { LayoutService } from '../../services/layout/layout.service'; -import { LoggerService } from '../../services/logger/logger.service'; -import { OpenViduService } from '../../services/openvidu/openvidu.service'; -import { PanelService } from '../../services/panel/panel.service'; -import { ParticipantService } from '../../services/participant/participant.service'; -import { PlatformService } from '../../services/platform/platform.service'; -import { RecordingService } from '../../services/recording/recording.service'; -import { TranslateService } from '../../services/translate/translate.service'; -import { VirtualBackgroundService } from '../../services/virtual-background/virtual-background.service'; - -/** - * @internal - */ - -@Component({ - selector: 'ov-session', - templateUrl: './session.component.html', - styleUrls: ['./session.component.css'], - animations: [trigger('sessionAnimation', [transition(':enter', [style({ opacity: 0 }), animate('50ms', style({ opacity: 1 }))])])], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class SessionComponent implements OnInit, OnDestroy { - @ContentChild('toolbar', { read: TemplateRef }) toolbarTemplate: TemplateRef; - @ContentChild('panel', { read: TemplateRef }) panelTemplate: TemplateRef; - @ContentChild('layout', { read: TemplateRef }) layoutTemplate: TemplateRef; - @Input() usedInPrejoinPage = false; - @Output() onNodeCrashed = new EventEmitter(); - - session: Session; - sessionScreen: Session; - sideMenu: MatSidenav; - sidenavMode: SidenavMode = SidenavMode.SIDE; - settingsPanelOpened: boolean; - drawer: MatDrawerContainer; - preparing: boolean = true; - - private isSessionCreator: boolean = false; - protected readonly SIDENAV_WIDTH_LIMIT_MODE = 790; - protected menuSubscription: Subscription; - protected layoutWidthSubscription: Subscription; - protected updateLayoutInterval: NodeJS.Timeout; - private captionLanguageSubscription: Subscription; - protected log: ILogger; - - constructor( - protected actionService: ActionService, - protected openviduService: OpenViduService, - protected participantService: ParticipantService, - protected loggerSrv: LoggerService, - protected chatService: ChatService, - private libService: OpenViduAngularConfigService, - protected layoutService: LayoutService, - protected panelService: PanelService, - private recordingService: RecordingService, - private broadcastingService: BroadcastingService, - private translateService: TranslateService, - private captionService: CaptionService, - private platformService: PlatformService, - private backgroundService: VirtualBackgroundService, - private cd: ChangeDetectorRef - ) { - this.log = this.loggerSrv.get('SessionComponent'); - } - - @HostListener('window:beforeunload') - beforeunloadHandler() { - this.leaveSession(); - } - - @HostListener('window:resize') - sizeChange() { - this.layoutService.update(); - } - - @ViewChild('sidenav') - set sidenavMenu(menu: MatSidenav) { - setTimeout(() => { - if (menu) { - this.sideMenu = menu; - this.subscribeToTogglingMenu(); - } - }, 0); - } - - @ViewChild('videoContainer', { static: false, read: ElementRef }) - set videoContainer(container: ElementRef) { - setTimeout(() => { - if (container && !this.toolbarTemplate) { - container.nativeElement.style.height = '100%'; - container.nativeElement.style.minHeight = '100%'; - this.layoutService.update(); - } - }, 0); - } - - @ViewChild('container') - set container(container: MatDrawerContainer) { - setTimeout(() => { - if (container) { - this.drawer = container; - this.drawer._contentMarginChanges.subscribe(() => { - setTimeout(() => { - this.stopUpdateLayoutInterval(); - this.layoutService.update(); - this.drawer.autosize = false; - }, 250); - }); - } - }, 0); - } - - @ViewChild('layoutContainer') - set layoutContainer(container: ElementRef) { - setTimeout(async () => { - if (container) { - // Apply background from storage when layout container is in DOM - await this.backgroundService.applyBackgroundFromStorage(); - } - }, 0); - } - - async ngOnInit() { - if (!this.usedInPrejoinPage) { - this.isSessionCreator = this.participantService.amIModerator(); - if (!this.openviduService.getScreenToken()) { - // Hide screenshare button if screen token does not exist - this.libService.screenshareButton.next(false); - } - this.session = this.openviduService.getWebcamSession(); - this.sessionScreen = this.openviduService.getScreenSession(); - - this.subscribeToOpenViduException(); - this.subscribeToCaptionLanguage(); - this.subscribeToConnectionCreatedAndDestroyed(); - this.subscribeToStreamCreated(); - this.subscribeToStreamDestroyed(); - this.subscribeToStreamPropertyChange(); - this.subscribeToNicknameChanged(); - this.chatService.subscribeToChat(); - this.subscribeToReconnection(); - - await this.connectToSession(); - - if (this.libService.isRecordingEnabled()) { - this.subscribeToRecordingEvents(); - } - - if (this.libService.isBroadcastingEnabled()) { - this.subscribeToBroadcastingEvents(); - } - } - this.preparing = false; - this.cd.markForCheck(); - } - - async ngOnDestroy() { - // Reconnecting session is received in Firefox - // To avoid 'Connection lost' message uses session.off() - if (!this.usedInPrejoinPage) { - this.session?.off('reconnecting'); - await this.participantService.clear(); - this.session = null; - this.sessionScreen = null; - if (this.menuSubscription) this.menuSubscription.unsubscribe(); - if (this.layoutWidthSubscription) this.layoutWidthSubscription.unsubscribe(); - if (this.captionLanguageSubscription) this.captionLanguageSubscription.unsubscribe(); - } - } - - leaveSession() { - this.log.d('Leaving session...'); - this.openviduService.disconnect(); - } - - protected subscribeToTogglingMenu() { - this.sideMenu.openedChange.subscribe(() => { - this.stopUpdateLayoutInterval(); - this.layoutService.update(); - }); - - this.sideMenu.openedStart.subscribe(() => { - this.startUpdateLayoutInterval(); - }); - - this.sideMenu.closedStart.subscribe(() => { - this.startUpdateLayoutInterval(); - }); - - this.menuSubscription = this.panelService.panelOpenedObs.pipe(skip(1)).subscribe((ev: PanelEvent) => { - if (this.sideMenu) { - this.settingsPanelOpened = ev.opened && ev.type === PanelType.SETTINGS; - - if (this.sideMenu.opened && ev.opened) { - if (ev.type === PanelType.SETTINGS || ev.oldType === PanelType.SETTINGS) { - // Switch from SETTINGS to another panel and vice versa. - // As the SETTINGS panel will be bigger than others, the sidenav container must be updated. - // Setting autosize to 'true' allows update it. - this.drawer.autosize = true; - this.startUpdateLayoutInterval(); - } - } - ev.opened ? this.sideMenu.open() : this.sideMenu.close(); - } - }); - } - - protected subscribeToLayoutWidth() { - this.layoutWidthSubscription = this.layoutService.layoutWidthObs.subscribe((width) => { - this.sidenavMode = width <= this.SIDENAV_WIDTH_LIMIT_MODE ? SidenavMode.OVER : SidenavMode.SIDE; - }); - } - - private async connectToSession(): Promise { - try { - const participant = this.participantService.getLocalParticipant(); - const nickname = participant.getNickname(); - const participantId = participant.id; - const screenPublisher = this.participantService.getMyScreenPublisher(); - const cameraPublisher = this.participantService.getMyCameraPublisher(); - - - if (participant.hasCameraAndScreenActives()) { - - const webcamSessionId = await this.openviduService.connectWebcamSession(participantId, nickname); - if (webcamSessionId) this.participantService.setMyCameraConnectionId(webcamSessionId); - - const screenSessionId = await this.openviduService.connectScreenSession(participantId, nickname); - if (screenSessionId) this.participantService.setMyScreenConnectionId(screenSessionId); - - await this.openviduService.publishCamera(cameraPublisher); - await this.openviduService.publishScreen(screenPublisher); - } else if (participant.hasOnlyScreenActive()) { - await this.openviduService.connectScreenSession(participantId, nickname); - await this.openviduService.publishScreen(screenPublisher); - } else { - await this.openviduService.connectWebcamSession(participantId, nickname); - await this.openviduService.publishCamera(cameraPublisher); - } - } catch (error) { - // this._error.emit({ error: error.error, messgae: error.message, code: error.code, status: error.status }); - this.log.e('There was an error connecting to the session:', error.code, error.message); - this.actionService.openDialog(this.translateService.translate('ERRORS.SESSION'), error?.error || error?.message || error); - } - } - - private subscribeToOpenViduException() { - this.session.on('exception', async (event: ExceptionEvent) => { - if (event.name === ExceptionEventName.SPEECH_TO_TEXT_DISCONNECTED) { - this.log.w(event.name, event.message); - this.openviduService.setSTTReady(false); - // Try to re-subscribe to STT - await this.openviduService.subscribeRemotesToSTT(this.captionService.getLangSelected().lang); - } else { - this.log.e(event.name, event.message); - } - }); - } - - private subscribeToConnectionCreatedAndDestroyed() { - this.session.on('connectionCreated', async (event: ConnectionEvent) => { - const connectionId = event.connection?.connectionId; - const connectionNickname: string = this.participantService.getNicknameFromConnectionData(event.connection.data); - const isRemoteConnection: boolean = !this.openviduService.isMyOwnConnection(connectionId); - const isCameraConnection: boolean = !connectionNickname?.includes(`_${VideoType.SCREEN}`); - const nickname = this.participantService.getMyNickname(); - const data = event.connection?.data; - - if (isRemoteConnection && isCameraConnection) { - // Adding participant when connection is created and it's not screen - this.participantService.addRemoteConnection(connectionId, data, null); - - //Sending nicnkanme signal to new connection - if (this.openviduService.myNicknameHasBeenChanged()) { - await this.openviduService.sendSignal(Signal.NICKNAME_CHANGED, [event.connection], { clientData: nickname }); - } - } - }); - - this.session.on('connectionDestroyed', (event: ConnectionEvent) => { - const nickname: string = this.participantService.getNicknameFromConnectionData(event.connection.data); - const isRemoteConnection: boolean = !this.openviduService.isMyOwnConnection(event.connection.connectionId); - const isCameraConnection: boolean = !nickname?.includes(`_${VideoType.SCREEN}`); - // Deleting participant when connection is destroyed - if (isRemoteConnection && isCameraConnection) { - this.participantService.removeConnectionByConnectionId(event.connection.connectionId); - } - }); - } - - private subscribeToStreamCreated() { - this.session.on('streamCreated', async (event: StreamEvent) => { - const connectionId = event.stream?.connection?.connectionId; - const data = event.stream?.connection?.data; - const isCameraType: boolean = this.participantService.getTypeConnectionData(data) === VideoType.CAMERA; - const isRemoteConnection: boolean = !this.openviduService.isMyOwnConnection(connectionId); - const lang = this.captionService.getLangSelected().lang; - - if (isRemoteConnection) { - const subscriber: Subscriber = this.session.subscribe(event.stream, undefined); - this.participantService.addRemoteConnection(connectionId, data, subscriber); - // this.oVSessionService.sendNicknameSignal(event.stream.connection); - - if (this.openviduService.isSttReady() && this.captionService.areCaptionsEnabled() && isCameraType) { - // Only subscribe to STT when is ready and stream is CAMERA type and it is a remote stream - try { - await this.openviduService.subscribeStreamToStt(event.stream, lang); - } catch (error) { - this.log.e('Error subscribing from STT: ', error); - // I assume the only reason of an STT error is a STT crash. - // It must be subscribed to all remotes again - // await this.openviduService.unsubscribeRemotesFromSTT(); - await this.openviduService.subscribeRemotesToSTT(lang); - } - } - } - }); - } - - private subscribeToStreamDestroyed() { - this.session.on('streamDestroyed', async (event: StreamEvent) => { - const connectionId = event.stream.connection.connectionId; - const data = event.stream?.connection?.data; - const isRemoteConnection: boolean = !this.openviduService.isMyOwnConnection(connectionId); - const isCameraType: boolean = this.participantService.getTypeConnectionData(data) === VideoType.CAMERA; - - this.participantService.removeConnectionByConnectionId(connectionId); - if (this.openviduService.isSttReady() && this.captionService.areCaptionsEnabled() && isRemoteConnection && isCameraType) { - try { - await this.session.unsubscribeFromSpeechToText(event.stream); - } catch (error) { - this.log.e('Error unsubscribing from STT: ', error); - } - } - }); - } - - private subscribeToCaptionLanguage() { - this.captionLanguageSubscription = this.captionService.captionLangObs.subscribe(async (langOpt) => { - if (this.captionService.areCaptionsEnabled()) { - // Unsubscribe all streams from speech to text and re-subscribe with new language - this.log.d('Re-subscribe from STT because of language changed to ', langOpt.lang); - await this.openviduService.unsubscribeRemotesFromSTT(); - await this.openviduService.subscribeRemotesToSTT(langOpt.lang); - } - }); - } - - private subscribeToStreamPropertyChange() { - this.session.on('streamPropertyChanged', (event: StreamPropertyChangedEvent) => { - const connectionId = event.stream.connection.connectionId; - const isRemoteConnection: boolean = !this.openviduService.isMyOwnConnection(connectionId); - if (isRemoteConnection) { - this.participantService.updateRemoteParticipants(); - } - }); - } - - private subscribeToNicknameChanged() { - this.session.on(`signal:${Signal.NICKNAME_CHANGED}`, (event: any) => { - const connectionId = event.from.connectionId; - const isRemoteConnection: boolean = !this.openviduService.isMyOwnConnection(connectionId); - - if (isRemoteConnection) { - const nickname = this.participantService.getNicknameFromConnectionData(event.data); - this.participantService.setRemoteNickname(connectionId, nickname); - } - }); - } - - private subscribeToReconnection() { - this.session.on('reconnecting', () => { - this.log.w('Connection lost: Reconnecting'); - this.actionService.openDialog( - this.translateService.translate('ERRORS.CONNECTION'), - this.translateService.translate('ERRORS.RECONNECT'), - false - ); - }); - this.session.on('reconnected', () => { - this.log.w('Connection lost: Reconnected'); - this.actionService.closeDialog(); - }); - this.session.on('sessionDisconnected', (event: SessionDisconnectedEvent) => { - if (event.reason === 'nodeCrashed') { - this.actionService.openDialog( - this.translateService.translate('ERRORS.CONNECTION'), - this.translateService.translate('ERRORS.RECONNECT'), - false - ); - this.onNodeCrashed.emit(); - } else if (event.reason === 'networkDisconnect') { - this.actionService.closeDialog(); - this.leaveSession(); - } - }); - } - - private subscribeToRecordingEvents() { - this.session.on('recordingStarted', (event: RecordingEvent) => this.recordingService.startRecording(event)); - - this.session.on('recordingStopped', (event: RecordingEvent) => this.recordingService.stopRecording(event)); - } - - private subscribeToBroadcastingEvents() { - this.session.on('broadcastStarted', () => this.broadcastingService.startBroadcasting()); - this.session.on('broadcastStopped', () => this.broadcastingService.stopBroadcasting()); - - if (!this.isSessionCreator) { - // Listen to recording delete events from moderator - this.session.on(`signal:${Signal.RECORDING_DELETED}`, () => this.recordingService.forceUpdateRecordings()); - } - } - - private startUpdateLayoutInterval() { - this.updateLayoutInterval = setInterval(() => { - this.layoutService.update(); - }, 50); - } - - private stopUpdateLayoutInterval() { - if (this.updateLayoutInterval) { - clearInterval(this.updateLayoutInterval); - } - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.css b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.css deleted file mode 100644 index 595a9382..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.css +++ /dev/null @@ -1,17 +0,0 @@ -.device-container-element mat-form-field { - width: 100%; - margin-top: 10px; - color: #000000; -} -.device-container-element button { - margin: auto 10px auto auto; -} - -.device-container-element { - display: flex; -} - -.warn-btn { - color: var(--ov-text-color); - background-color: var(--ov-warn-color) !important; -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.html b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.html deleted file mode 100644 index 3ae03693..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.html +++ /dev/null @@ -1,34 +0,0 @@ -
- - - {{ 'PREJOIN.AUDIO_DEVICE' | translate }} - {{ 'PREJOIN.NO_AUDIO_DEVICE' | translate }} - - - {{ microphone.label }} - - - -
diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.ts deleted file mode 100644 index 2d1b51ab..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core'; -import { PublisherProperties } from 'openvidu-browser-v2compatibility'; -import { Subscription } from 'rxjs'; -import { CustomDevice } from '../../../models/device.model'; -import { ParticipantAbstractModel } from '../../../models/participant.model'; -import { DeviceService } from '../../../services/device/device.service'; -import { OpenViduService } from '../../../services/openvidu/openvidu.service'; -import { ParticipantService } from '../../../services/participant/participant.service'; -import { StorageService } from '../../../services/storage/storage.service'; - -/** - * @internal - */ -@Component({ - selector: 'ov-audio-devices-select', - templateUrl: './audio-devices.component.html', - styleUrls: ['./audio-devices.component.css'] -}) -export class AudioDevicesComponent implements OnInit, OnDestroy { - @Output() onDeviceSelectorClicked = new EventEmitter(); - @Output() onAudioMutedClicked = new EventEmitter(); - hasAudioDevices: boolean; - isAudioMuted: boolean; - microphoneSelected: CustomDevice | null; - microphones: CustomDevice[] = []; - private localParticipantSubscription: Subscription; - - constructor( - private openviduService: OpenViduService, - private deviceSrv: DeviceService, - private storageSrv: StorageService, - private participantService: ParticipantService - ) {} - - async ngOnInit() { - this.subscribeToParticipantMediaProperties(); - if (this.openviduService.isSessionConnected()) { - // Updating devices only with session connected - await this.deviceSrv.refreshDevices(); - } - this.hasAudioDevices = this.deviceSrv.hasAudioDeviceAvailable(); - if(this.hasAudioDevices) { - this.microphones = this.deviceSrv.getMicrophones(); - this.microphoneSelected = this.deviceSrv.getMicrophoneSelected(); - } - this.isAudioMuted = this.deviceSrv.isAudioMuted(); - if (this.openviduService.isSessionConnected()) { - this.isAudioMuted = !this.participantService.isMyAudioActive(); - } else { - this.isAudioMuted = this.deviceSrv.isAudioMuted(); - } - } - - ngOnDestroy() { - if (this.localParticipantSubscription) this.localParticipantSubscription.unsubscribe(); - } - - toggleMic() { - const publish = this.isAudioMuted; - this.participantService.publishAudio(publish); - this.onAudioMutedClicked.emit(publish); - } - - async onMicrophoneSelected(event: any) { - const audioSource = event?.value; - if (this.deviceSrv.needUpdateAudioTrack(audioSource)) { - const pp: PublisherProperties = { audioSource, videoSource: false }; - const publisher = this.participantService.getMyCameraPublisher(); - await this.openviduService.replaceCameraTrack(publisher, pp); - this.deviceSrv.setMicSelected(audioSource); - this.microphoneSelected = this.deviceSrv.getMicrophoneSelected(); - } - } - - private subscribeToParticipantMediaProperties() { - this.localParticipantSubscription = this.participantService.localParticipantObs.subscribe((p: ParticipantAbstractModel) => { - if (p) { - this.isAudioMuted = !p.hasAudioActive(); - this.storageSrv.setAudioMuted(this.isAudioMuted); - } - }); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/captions/captions.component.css b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/captions/captions.component.css deleted file mode 100644 index 1d4a2d1b..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/captions/captions.component.css +++ /dev/null @@ -1,27 +0,0 @@ -#lang-section { - margin-top: 10px; -} - -.lang-section-item { - height: fit-content !important -} - -.section-description { - color: var(--ov-panel-text-color); - font-size: 12px; - padding: 0px 5px; - display: block; -} - -.lang-button { - background-color: var(--ov-logo-background-color); - color: var(--ov-text-color); -} - -.pro-feature { - text-align: center; -} - -.pro-feture p { - font-size: 14px; -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.css b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.css deleted file mode 100644 index f9c8389f..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.css +++ /dev/null @@ -1,7 +0,0 @@ -.lang-button { - background-color: var(--ov-logo-background-color); - color: var(--ov-text-color); -} -mat-icon { - vertical-align: middle; -} \ No newline at end of file diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.html b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.html deleted file mode 100644 index ee51d649..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/nickname-input/nickname-input.component.css b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/nickname-input/nickname-input.component.css deleted file mode 100644 index c15d221b..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/nickname-input/nickname-input.component.css +++ /dev/null @@ -1,21 +0,0 @@ -#nickname-input-container { - display: flex; -} - -#nickname-input-container button { - margin: auto 10px auto auto; -} - -#nickname-button { - color: #000000 !important; -} - -#nickname-input-container mat-form-field { - width: 100%; - margin-top: 10px; - color: #000000; -} - -#nickname-input-container mat-form-field { - color: #000000; -} \ No newline at end of file diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/nickname-input/nickname-input.component.html b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/nickname-input/nickname-input.component.html deleted file mode 100644 index 69f7cb0a..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/nickname-input/nickname-input.component.html +++ /dev/null @@ -1,16 +0,0 @@ -
- - - {{ 'PREJOIN.NICKNAME' | translate }} - - -
\ No newline at end of file diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/nickname-input/nickname-input.component.spec.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/nickname-input/nickname-input.component.spec.ts deleted file mode 100644 index 7faded0c..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/nickname-input/nickname-input.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { NicknameInputComponent } from './nickname-input.component'; - -describe('NicknameInputComponent', () => { - let component: NicknameInputComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ NicknameInputComponent ] - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(NicknameInputComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/nickname-input/nickname-input.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/nickname-input/nickname-input.component.ts deleted file mode 100644 index 9ea707c6..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/nickname-input/nickname-input.component.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { Subscription } from 'rxjs'; -import { ParticipantAbstractModel } from '../../../models/participant.model'; -import { ParticipantService } from '../../../services/participant/participant.service'; -import { StorageService } from '../../../services/storage/storage.service'; - - -/** - * @internal - */ -@Component({ - selector: 'ov-nickname-input', - templateUrl: './nickname-input.component.html', - styleUrls: ['./nickname-input.component.css'] -}) -export class NicknameInputComponent implements OnInit { - nickname: string; - localParticipantSubscription: Subscription; - - constructor(private participantService: ParticipantService, private storageSrv: StorageService) {} - - ngOnInit(): void { - this.subscribeToParticipantProperties(); - this.nickname = this.participantService.getMyNickname(); - } - - updateNickname() { - this.nickname = this.nickname === '' ? this.participantService.getMyNickname() : this.nickname; - this.participantService.setMyNickname(this.nickname); - this.storageSrv.setNickname(this.nickname); - } - - private subscribeToParticipantProperties() { - this.localParticipantSubscription = this.participantService.localParticipantObs.subscribe((p: ParticipantAbstractModel) => { - if (p) { - this.nickname = p.getNickname(); - } - }); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.css b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.css deleted file mode 100644 index 86427b3b..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.css +++ /dev/null @@ -1,25 +0,0 @@ -#camera-button { - border-radius: var(--ov-buttons-radius); - /* background-color: var(--ov-secondary-color) !important; */ - /* color: var(--ov-text-color) !important; */ -} - -.device-container-element mat-form-field { - width: 100%; - margin-top: 10px; - color: #000000; -} - -.device-container-element button { - margin: auto 10px auto auto; -} - -.device-container-element { - display: flex; -} - -.warn-btn { - color: var(--ov-text-color); - background-color: var(--ov-warn-color) !important; -} - diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.html b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.html deleted file mode 100644 index c469f4ed..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.html +++ /dev/null @@ -1,41 +0,0 @@ -
- - - {{ 'PREJOIN.VIDEO_DEVICE' | translate }} - {{ 'PREJOIN.NO_VIDEO_DEVICE' | translate }} - - - {{ camera.label }} - - - -
diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.ts deleted file mode 100644 index 501ad45a..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core'; -import { PublisherProperties } from 'openvidu-browser-v2compatibility'; -import { Subscription } from 'rxjs'; -import { CustomDevice } from '../../../models/device.model'; -import { PanelType } from '../../../models/panel.model'; -import { ParticipantAbstractModel } from '../../../models/participant.model'; -import { DeviceService } from '../../../services/device/device.service'; -import { OpenViduService } from '../../../services/openvidu/openvidu.service'; -import { PanelService } from '../../../services/panel/panel.service'; -import { ParticipantService } from '../../../services/participant/participant.service'; -import { StorageService } from '../../../services/storage/storage.service'; -import { VirtualBackgroundService } from '../../../services/virtual-background/virtual-background.service'; - -/** - * @internal - */ -@Component({ - selector: 'ov-video-devices-select', - templateUrl: './video-devices.component.html', - styleUrls: ['./video-devices.component.css'] -}) -export class VideoDevicesComponent implements OnInit, OnDestroy { - @Output() onDeviceSelectorClicked = new EventEmitter(); - @Output() onVideoMutedClicked = new EventEmitter(); - - videoMuteChanging: boolean; - isVideoMuted: boolean; - cameraSelected: CustomDevice | null; - hasVideoDevices: boolean; - cameras: CustomDevice[] = []; - localParticipantSubscription: Subscription; - - constructor( - private openviduService: OpenViduService, - protected panelService: PanelService, - private storageSrv: StorageService, - private deviceSrv: DeviceService, - protected participantService: ParticipantService, - private backgroundService: VirtualBackgroundService - ) {} - - async ngOnInit() { - this.subscribeToParticipantMediaProperties(); - if (this.openviduService.isSessionConnected()) { - // Updating devices only with session connected - await this.deviceSrv.refreshDevices(); - } - - this.hasVideoDevices = this.deviceSrv.hasVideoDeviceAvailable(); - if (this.hasVideoDevices) { - this.cameras = this.deviceSrv.getCameras(); - this.cameraSelected = this.deviceSrv.getCameraSelected(); - } - if (this.openviduService.isSessionConnected()) { - this.isVideoMuted = !this.participantService.getLocalParticipant().isCameraVideoActive(); - } else { - this.isVideoMuted = this.deviceSrv.isVideoMuted(); - } - } - async ngOnDestroy() { - this.cameras = []; - if (this.localParticipantSubscription) this.localParticipantSubscription.unsubscribe(); - } - - async toggleCam() { - this.videoMuteChanging = true; - const publish = this.isVideoMuted; - await this.participantService.publishVideo(publish); - if (this.isVideoMuted && this.panelService.isExternalPanelOpened()) { - this.panelService.togglePanel(PanelType.BACKGROUND_EFFECTS); - } - this.videoMuteChanging = false; - this.onVideoMutedClicked.emit(publish); - } - - async onCameraSelected(event: any) { - const device: CustomDevice = event?.value; - - // Is New deviceId different from the old one? - if (this.deviceSrv.needUpdateVideoTrack(device)) { - // Reapply Virtual Background to new Publisher if necessary - const backgroundSelected = this.backgroundService.backgroundSelected.getValue(); - const isBackgroundApplied = this.backgroundService.isBackgroundApplied(); - - if (isBackgroundApplied) { - await this.backgroundService.removeBackground(); - } - - await this.participantService.replaceVideoTrack(device); - - if (isBackgroundApplied) { - const bgSelected = this.backgroundService.backgrounds.find((b) => b.id === backgroundSelected); - if (bgSelected) { - await this.backgroundService.applyBackground(bgSelected); - } - } - - this.deviceSrv.setCameraSelected(device.device); - this.cameraSelected = this.deviceSrv.getCameraSelected(); - } - } - - /** - * @internal - * Compare two devices to check if they are the same. Used by the mat-select - */ - compareObjectDevices(o1: CustomDevice, o2: CustomDevice): boolean { - return o1.label === o2.label; - } - - protected subscribeToParticipantMediaProperties() { - this.localParticipantSubscription = this.participantService.localParticipantObs.subscribe((p: ParticipantAbstractModel) => { - if (p) { - this.isVideoMuted = !p.isCameraVideoActive(); - this.storageSrv.setVideoMuted(this.isVideoMuted); - } - }); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/stream/stream.component.css b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/stream/stream.component.css deleted file mode 100644 index 6ec1f482..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/stream/stream.component.css +++ /dev/null @@ -1,132 +0,0 @@ -/* Fixes layout bug. The OT_root is created with the entire layout width and it has a weird UX behaviour */ -.no-size { - height: 0px !important; - width: 0px !important; -} - -.nickname { - padding: 0px; - position: absolute; - z-index: 9999; - border-radius: var(--ov-video-radius); - color: var(--ov-text-color); - font-family: 'Roboto', 'RobotoDraft', Helvetica, Arial, sans-serif; -} -.nicknameContainer { - background-color: var(--ov-secondary-color); - padding: 5px; - color: var(--ov-text-color); - font-weight: bold; - border-radius: var(--ov-video-radius); -} - -#nickname-input-container { - background-color: var(--ov-secondary-color); - border-radius: var(--ov-video-radius); -} - -#closeButton { - position: absolute; - top: -3px; - right: 0; - z-index: 999; -} - -#nicknameForm { - padding: 10px; -} - -#audio-wave-container { - position: absolute; - right: 0; - z-index: 9999; - padding: 5px; -} - -.fullscreen { - top: 40px; -} - -.video-settings-btn mat-icon { - position: absolute; - top: 5px; - bottom: 0; - left: 0; - right: 0; - margin: auto; -} - -video { - -o-object-fit: cover; - object-fit: cover; - width: 100%; - height: 100%; - color: #ffffff; - margin: 0; - padding: 0; - border: 0; - font-size: 100%; -} - -.status-icons, -#settings-container { - position: absolute; - bottom: 0; - z-index: 999; - text-align: center; -} - -.status-icons { - left: 0; -} - -.status-icons button, -#settings-container button { - color: var(--ov-text-color); - width: 26px; - height: 26px; - margin: 5px; - border-radius: var(--ov-buttons-radius); -} - -.status-icons button { - background-color: var(--ov-warn-color); -} - -.status-icons .mat-mdc-icon-button, -#settings-container .mat-icon-button { - line-height: 0px; -} - -.status-icons mat-icon, -#settings-container mat-icon { - font-size: 18px; - position: absolute; - top: 5px; - bottom: 0; - left: 0; - right: 0; - margin: auto; -} - -#settings-container { - right: 0; -} - -#settings-container button { - background-color: var(--ov-secondary-color); -} - -/* Contains the video element, used to fix video letter-boxing */ -.OV_stream { - width: 100%; - height: 100%; - position: relative; - overflow: hidden; - background-color: transparent; - border-radius: var(--ov-video-radius); -} - -input { - caret-color: #ffffff !important; -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/stream/stream.component.html b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/stream/stream.component.html deleted file mode 100644 index 388468c7..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/stream/stream.component.html +++ /dev/null @@ -1,95 +0,0 @@ -
-
-
- {{ this._stream.participant.nickname }} - {{ this._stream.participant.nickname }}_SCREEN -
-
- -
-
- -
- -
- - - - - -
- -
- -
- - - - - - - -
-
diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/stream/stream.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/stream/stream.component.ts deleted file mode 100644 index 638ef3d7..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/stream/stream.component.ts +++ /dev/null @@ -1,289 +0,0 @@ -import { animate, style, transition, trigger } from '@angular/animations'; -import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; -import { MatMenuPanel, MatMenuTrigger } from '@angular/material/menu'; -import { PublisherProperties } from 'openvidu-browser-v2compatibility'; -import { Subscription } from 'rxjs'; -import { VideoSizeIcon } from '../../models/icon.model'; -import { StreamModel } from '../../models/participant.model'; -import { Signal } from '../../models/signal.model'; -import { ScreenType, VideoType } from '../../models/video-type.model'; -import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.service'; -import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; -import { LayoutService } from '../../services/layout/layout.service'; -import { OpenViduService } from '../../services/openvidu/openvidu.service'; -import { ParticipantService } from '../../services/participant/participant.service'; -import { StorageService } from '../../services/storage/storage.service'; - -/** - * The **StreamComponent** is hosted inside of the {@link LayoutComponent}. - * It is in charge of displaying the participant video stream in the videoconference layout. - * - *
- *
- *

API Directives

- * - * This component allows us to show or hide certain HTML elements with the following {@link https://angular.io/guide/attribute-directives Angular attribute directives} - * with the aim of fully customizing the StreamComponent. - * - * | **Parameter** | **Type** | **Reference** | - * | :----------------------------: | :-------: | :---------------------------------------------: | - * | **displayParticipantName** | `boolean` | {@link StreamDisplayParticipantNameDirective} | - * | **displayAudioDetection** | `boolean` | {@link StreamDisplayAudioDetectionDirective} | - * | **settingsButton** | `boolean` | {@link StreamSettingsButtonDirective} | - * | **resolution** | `string` | {@link StreamResolutionDirective} | - * | **frameRate** | `number` | {@link StreamFrameRateDirective} | - * - * - *
- * - *
- *

OpenVidu Angular Directives

- * - * The StreamComponent can be replaced with a custom component. It provides us the following {@link https://angular.io/guide/structural-directives Angular structural directives} - * for doing this. - * - * | **Directive** | **Reference** | - * |:----------------------------------:|:---------------------------------------------:| - * | ***ovStream** | {@link StreamDirective} | - * - * - *
- * - *
- */ -@Component({ - selector: 'ov-stream', - templateUrl: './stream.component.html', - styleUrls: ['./stream.component.css'], - animations: [ - trigger('posterAnimation', [ - transition(':enter', [style({ opacity: 0 }), animate('100ms', style({ opacity: 1 }))]), - transition(':leave', [style({ opacity: 1 }), animate('200ms', style({ opacity: 0 }))]) - ]) - ] -}) -export class StreamComponent implements OnInit { - /** - * @ignore - */ - @ViewChild(MatMenuTrigger) public menuTrigger: MatMenuTrigger; - - /** - * @ignore - */ - @ViewChild('menu') menu: MatMenuPanel; - - /** - * @ignore - */ - videoSizeIconEnum = VideoSizeIcon; - /** - * @ignore - */ - videoTypeEnum = VideoType; - /** - * @ignore - */ - videoSizeIcon: VideoSizeIcon = VideoSizeIcon.BIG; - /** - * @ignore - */ - toggleNickname: boolean; - /** - * @ignore - */ - _stream: StreamModel; - /** - * @ignore - */ - nickname: string; - /** - * @ignore - */ - isMinimal: boolean = false; - /** - * @ignore - */ - showNickname: boolean = true; - /** - * @ignore - */ - showAudioDetection: boolean = true; - /** - * @ignore - */ - showSettingsButton: boolean = true; - showVideo: boolean; - - /** - * @ignore - */ - @ViewChild('streamContainer', { static: false, read: ElementRef }) - set streamContainer(streamContainer: ElementRef) { - setTimeout(() => { - if (streamContainer) { - this._streamContainer = streamContainer; - // This is a workaround for fixing a layout bug which provide a bad UX with each new elements created. - setTimeout(() => { - this.showVideo = true; - }, 100); - } - }, 0); - } - - @Input() - set stream(stream: StreamModel) { - this._stream = stream; - this.checkVideoEnlarged(); - if (this._stream.participant) { - this.nickname = this._stream.participant.nickname; - } - - } - - /** - * @ignore - */ - @ViewChild('nicknameInput') - set nicknameInputElement(element: ElementRef) { - setTimeout(() => { - element?.nativeElement.focus(); - }); - } - - private _streamContainer: ElementRef; - private minimalSub: Subscription; - private displayParticipantNameSub: Subscription; - private displayAudioDetectionSub: Subscription; - private settingsButtonSub: Subscription; - - /** - * @ignore - */ - constructor( - protected openviduService: OpenViduService, - protected layoutService: LayoutService, - protected participantService: ParticipantService, - protected storageService: StorageService, - protected cdkSrv: CdkOverlayService, - private libService: OpenViduAngularConfigService - ) {} - - ngOnInit() { - this.subscribeToStreamDirectives(); - } - - async ngAfterViewInit() { - if (this._stream.streamManager) { - await this.openviduService.updateVideoEncodingParameters(this._stream.streamManager); - } - } - - ngOnDestroy() { - this.cdkSrv.setSelector('body'); - if (this.settingsButtonSub) this.settingsButtonSub.unsubscribe(); - if (this.displayAudioDetectionSub) this.displayAudioDetectionSub.unsubscribe(); - if (this.displayParticipantNameSub) this.displayParticipantNameSub.unsubscribe(); - if (this.minimalSub) this.minimalSub.unsubscribe(); - } - - /** - * @ignore - */ - toggleVideoEnlarged() { - const connectionId = this._stream.streamManager?.stream?.connection?.connectionId; - if (Boolean(connectionId)) { - if (this.openviduService.isMyOwnConnection(connectionId)) { - this.participantService.toggleMyVideoEnlarged(connectionId); - } else { - this.participantService.toggleRemoteVideoEnlarged(connectionId); - } - this.checkVideoEnlarged(); - this.layoutService.update(); - } - } - - /** - * @ignore - */ - toggleVideoMenu(event) { - if (this.menuTrigger.menuOpen) { - this.menuTrigger.closeMenu(); - return; - } - this.cdkSrv.setSelector('#container-' + this._stream.streamManager?.stream?.streamId); - this.menuTrigger.openMenu(); - } - - /** - * @ignore - */ - toggleMuteForcibly() { - if (this._stream.participant) { - this.participantService.setRemoteMutedForcibly(this._stream.participant.id, !this._stream.participant.isMutedForcibly); - } - } - - /** - * @ignore - */ - toggleNicknameForm() { - if (this._stream?.participant?.local) { - this.toggleNickname = !this.toggleNickname; - } - } - - /** - * @ignore - */ - async updateNickname(event) { - if (event?.keyCode === 13 || event?.type === 'focusout') { - if (!!this.nickname) { - this.participantService.setMyNickname(this.nickname); - this.storageService.setNickname(this.nickname); - await this.openviduService.sendSignal(Signal.NICKNAME_CHANGED, undefined, { clientData: this.nickname }); - } - this.toggleNicknameForm(); - } - } - - /** - * @ignore - */ - async replaceScreenTrack() { - const properties: PublisherProperties = { - videoSource: ScreenType.SCREEN, - publishVideo: true, - publishAudio: !this.participantService.isMyCameraActive(), - mirror: false - }; - const publisher = this.participantService.getMyScreenPublisher(); - await this.openviduService.replaceScreenTrack(publisher, properties); - } - - private checkVideoEnlarged() { - this.videoSizeIcon = this._stream.videoEnlarged ? VideoSizeIcon.NORMAL : VideoSizeIcon.BIG; - } - - private subscribeToStreamDirectives() { - this.minimalSub = this.libService.minimalObs.subscribe((value: boolean) => { - this.isMinimal = value; - }); - this.displayParticipantNameSub = this.libService.displayParticipantNameObs.subscribe((value: boolean) => { - this.showNickname = value; - // this.cd.markForCheck(); - }); - this.displayAudioDetectionSub = this.libService.displayAudioDetectionObs.subscribe((value: boolean) => { - this.showAudioDetection = value; - // this.cd.markForCheck(); - }); - this.settingsButtonSub = this.libService.streamSettingsButtonObs.subscribe((value: boolean) => { - this.showSettingsButton = value; - // this.cd.markForCheck(); - }); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/toolbar/toolbar.component.css b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/toolbar/toolbar.component.css deleted file mode 100644 index cc62f019..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/toolbar/toolbar.component.css +++ /dev/null @@ -1,187 +0,0 @@ -#toolbar { - height: 100%; - background-color: transparent; - color: var(--ov-text-color); -} - -.buttonsContainer { - position: absolute; - left: 0; - right: 0; -} - -#info-container > div { - display: flex; - align-items: center; -} -#media-buttons-container { - max-height: 100% !important; -} - -#media-buttons-container > button, -::ng-deep #media-buttons-container > button, -#media-buttons-container:not(#media-buttons-container > button) button, -/* Applying css for external additional buttons*/ -::ng-deep #media-buttons-container:not(#media-buttons-container > button) button { - width: 48px; - height: 48px; - background-color: var(--ov-secondary-color); - margin: 6px; -} - -.warn-btn { - background-color: var(--ov-warn-color) !important; -} - -.active-btn, -::ng-deep .active-btn { - background-color: var(--ov-tertiary-color) !important; -} - -#media-buttons-container mat-icon { - font-size: 24px; -} - -#media-buttons-container button, -#menu-buttons-container button { - border-radius: var(--ov-buttons-radius); -} - -#branding-logo { - background-color: var(--ov-logo-background-color); - border-radius: var(--ov-panel-radius); - max-width: 35px; - max-height: 35px; - padding: 10px; -} - -#session-name { - font-family: 'Ubuntu', sans-serif; - font-weight: bold; - font-size: 15px; - height: fit-content; - padding: 0px 15px; -} -#session-info-container { - display: flex; -} - -.collapsed { - flex-direction: column; -} - -#activities-tag { - display: inline-flex; -} - -#activities-tag > div { - margin-left: 5px; -} -.recording-tag, -.broadcasting-tag { - padding: 0 10px; - border-radius: var(--ov-panel-radius); - width: fit-content; - font-size: 12px; - text-align: center; - line-height: 20px; - margin: auto; -} - -.recording-tag { - background-color: var(--ov-warn-color); -} -.broadcasting-tag { - background-color: #5903ca; -} - -.recording-tag mat-icon, -.broadcasting-tag mat-icon { - font-size: 16px; - display: inline; - vertical-align: sub; - margin-right: 5px; -} - -#broadcasting-btn > mat-icon { - color: #5903ca; -} - -.blink { - animation: blinker 1.5s linear infinite; -} - -#point { - width: 10px; - height: 10px; - position: absolute; - top: 12px; - right: 33px; - border-radius: var(--ov-buttons-radius); - background-color: #ffa600; - border: 1px solid #000; - z-index: 99999; -} - -#leave-btn { - background-color: var(--ov-warn-color) !important; - border-radius: var(--ov-leave-button-radius) !important; - width: 68px !important; -} - -.tooltipList { - white-space: pre; -} - -#navChatButton .mat-badge-medium.mat-badge-overlap.mat-badge-before .mat-badge-content { - left: -17px; -} - -#navChatButton .mat-badge-medium.mat-badge-above .mat-badge-content { - top: -6px; -} - -.mat-mdc-icon-button[disabled] { - color: #fff; -} -.divider { - margin: 8px 0px; -} -::ng-deep .mat-mdc-menu-item { - /* margin-bottom: 10px; */ - height: 40px; - line-height: 40px; -} - -@media (max-width: 750px) { - #session-name { - display: none; - } - - #activities-tag { - display: grid; - gap: 5px; - } -} - -@media (max-width: 850px) { - #branding-logo { - display: none; - } - #session-info-container { - display: inline-flex; - } -} - -@media (max-width: 550px) { - #toolbar { - padding: 0px; - place-content: space-evenly; - } -} - -@keyframes blinker { - 50% { - opacity: 0.3; - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/video/video.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/video/video.component.ts deleted file mode 100644 index fa06173a..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/video/video.component.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; -import { StreamManager } from 'openvidu-browser-v2compatibility'; -import { VideoType } from '../../models/video-type.model'; - -/** - * @internal - */ -@Component({ - selector: 'ov-video', - template: ` `, - styleUrls: ['./video.component.css'] -}) -export class VideoComponent implements AfterViewInit { - @Input() mutedSound: boolean; - @Input() participantId: string; - _streamManager: StreamManager; - _videoElement: ElementRef; - type: VideoType = VideoType.CAMERA; - - ngAfterViewInit() { - setTimeout(() => { - if (this._streamManager && this._videoElement) { - this.updateVideoStyles(); - this._streamManager.addVideoElement(this._videoElement.nativeElement); - } - }); - } - - @ViewChild('videoElement', { static: false }) - set videoElement(element: ElementRef) { - this._videoElement = element; - } - - @Input() - set streamManager(streamManager: StreamManager) { - if (streamManager) { - this._streamManager = streamManager; - if (!!this._videoElement && this._streamManager) { - this.updateVideoStyles(); - this._streamManager.addVideoElement(this._videoElement.nativeElement); - } - } - } - - private updateVideoStyles() { - this.type = this._streamManager?.stream?.typeOfVideo; - if (this.type === VideoType.SCREEN) { - this._videoElement.nativeElement.style.objectFit = 'contain'; - this._videoElement.nativeElement.classList.add('screen-type'); - this._videoElement.nativeElement.id =`video-screen-${this.participantId}`; - } else { - this._videoElement.nativeElement.style.objectFit = 'cover'; - this._videoElement.nativeElement.classList.add('camera-type'); - this._videoElement.nativeElement.id = `video-camera-${this.participantId}`; - } - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.css b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.css deleted file mode 100644 index 92035d1e..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.css +++ /dev/null @@ -1,31 +0,0 @@ -#call-container, #vc-container { - height: 100%; -} -#vc-container { - background-color: var(--ov-primary-color); -} - -#pre-join-container { - height: inherit; -} - -.error-icon { - color: var(--ov-warn-color); -} - -#spinner { - position: absolute; - top: 40%; - bottom: 0; - left: 0; - right: 0; - margin: auto; - text-align: -webkit-center; - text-align: -moz-center; - color: var(--ov-panel-text-color); -} - -/* Private css variables */ -#call-container { - --ov-captions-height: 230px; -} \ No newline at end of file diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.ts deleted file mode 100644 index 3305f5f3..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.ts +++ /dev/null @@ -1,806 +0,0 @@ -import { animate, style, transition, trigger } from '@angular/animations'; -import { - AfterViewInit, - Component, - ContentChild, - EventEmitter, - Input, - OnDestroy, - OnInit, - Output, - TemplateRef, - ViewChild -} from '@angular/core'; -import { OpenViduErrorName, Session } from 'openvidu-browser-v2compatibility'; -import { Subscription } from 'rxjs'; -import { - ActivitiesPanelDirective, - AdditionalPanelsDirective, - ChatPanelDirective, - LayoutDirective, - PanelDirective, - ParticipantPanelItemDirective, - ParticipantPanelItemElementsDirective, - ParticipantsPanelDirective, - StreamDirective, - ToolbarAdditionalButtonsDirective, - ToolbarAdditionalPanelButtonsDirective, - ToolbarDirective -} from '../../directives/template/openvidu-angular.directive'; -import { ILogger } from '../../models/logger.model'; -import { OpenViduEdition } from '../../models/openvidu.model'; -import { ParticipantAbstractModel } from '../../models/participant.model'; -import { TokenModel } from '../../models/token.model'; -import { ActionService } from '../../services/action/action.service'; -import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; -import { DeviceService } from '../../services/device/device.service'; -import { LoggerService } from '../../services/logger/logger.service'; -import { OpenViduService } from '../../services/openvidu/openvidu.service'; -import { ParticipantService } from '../../services/participant/participant.service'; -import { StorageService } from '../../services/storage/storage.service'; -import { TranslateService } from '../../services/translate/translate.service'; -import { LangOption } from '../../models/lang.model'; - -/** - * The **VideoconferenceComponent** is the parent of all OpenVidu components. - * It allow us to create a modern, useful and powerful videoconference apps with ease. - * - *
- *
- *

API Directives

- * - * This component allows us to show or hide certain HTML elements with the following {@link https://angular.io/guide/attribute-directives Angular attribute directives} - * with the aim of fully customizing the videoconference application. - * - * | **Parameter** | **Type** | **Reference** | - * | :----------------------------: | :-------: | :---------------------------------------------: | - * | **minimal** | `boolean` | {@link MinimalDirective} | - * | **lang** | `string` | {@link LangDirective} | - * | **langOptions** | `LangOption []` | {@link LangOptionsDirective} | - * | **captionsLang** | `string` | {@link CaptionsLangDirective} | - * | **captionsLangOptions** | `CaptionsLangOption []` | {@link CaptionsLangOptionsDirective} | - * | **prejoin** | `boolean` | {@link PrejoinDirective} | - * | **participantName** | `string` | {@link ParticipantNameDirective} | - * | **videoMuted** | `boolean` | {@link VideoMutedDirective} | - * | **audioMuted** | `boolean` | {@link AudioMutedDirective} | - | **simulcast** | `boolean` | {@link SimulcastDirective} | - * | **toolbarScreenshareButton** | `boolean` | {@link ToolbarScreenshareButtonDirective} | - * | **toolbarFullscreenButton** | `boolean` | {@link ToolbarFullscreenButtonDirective} | - * | **toolbarCaptionsButton** | `boolean` | {@link ToolbarCaptionsButtonDirective} | - * | **toolbarBackgroundEffectsButton** | `boolean` | {@link ToolbarBackgroundEffectsButtonDirective} | - * | **toolbarLeaveButton** | `boolean` | {@link ToolbarLeaveButtonDirective} | - * | **toolbarChatPanelButton** | `boolean` | {@link ToolbarChatPanelButtonDirective} | - * | **toolbarParticipantsPanelButton** | `boolean` | {@link ToolbarParticipantsPanelButtonDirective} | - * | **toolbarDisplayLogo** | `boolean` | {@link ToolbarDisplayLogoDirective} | - * | **toolbarDisplaySessionName** | `boolean` | {@link ToolbarDisplaySessionNameDirective} | - * | **streamDisplayParticipantName** | `boolean` | {@link StreamDisplayParticipantNameDirective} | - * | **streamDisplayAudioDetection** | `boolean` | {@link StreamDisplayAudioDetectionDirective} | - * | **streamSettingsButton** | `boolean` | {@link StreamSettingsButtonDirective} | - * | **streamFrameRate** | `number` | {@link StreamFrameRateDirective} | - * | **streamResolution** | `string` | {@link StreamResolutionDirective} | - * | **participantPanelItemMuteButton** | `boolean` | {@link ParticipantPanelItemMuteButtonDirective} | - * | **recordingActivityRecordingList** | `{@link RecordingInfo}[]` | {@link RecordingActivityRecordingsListDirective} | - * | **recordingActivityRecordingError** | `any` | {@link RecordingActivityRecordingErrorDirective} | - * - * - *
- * - *
- * - *

OpenVidu Angular Directives

- * - * - * The VideoconferenceComponent is also providing us a way to **replace the default templates** with a custom one. - * It will recognise the following {@link https://angular.io/guide/structural-directives Angular structural directives} - * in the elements added as children. - * - * | **Directive** | **Reference** | - * |:-----------------------------------:|:---------------------------------------------:| - * | ***ovToolbar** | {@link ToolbarDirective} | - * | ***ovToolbarAdditionalButtons** | {@link ToolbarAdditionalButtonsDirective} | - * |***ovToolbarAdditionalPanelButtons** | {@link ToolbarAdditionalPanelButtonsDirective} | - * | ***ovPanel** | {@link PanelDirective} | - * | ***ovAdditionalPanels** | {@link AdditionalPanelsDirective} | - * | ***ovChatPanel** | {@link ChatPanelDirective} | - * | ***ovParticipantsPanel** | {@link ParticipantsPanelDirective} | - * | ***ovParticipantPanelItem** | {@link ParticipantPanelItemDirective} | - * | ***ovParticipantPanelItemElements** | {@link ParticipantPanelItemElementsDirective} | - * | ***ovLayout** | {@link LayoutDirective} | - * | ***ovStream** | {@link StreamDirective} | - * - * - *
- *
- */ -@Component({ - selector: 'ov-videoconference', - templateUrl: './videoconference.component.html', - styleUrls: ['./videoconference.component.css'], - animations: [ - trigger('inOutAnimation', [ - transition(':enter', [style({ opacity: 0 }), animate('300ms ease-out', style({ opacity: 1 }))]) - // transition(':leave', [style({ opacity: 1 }), animate('50ms ease-in', style({ opacity: 0.9 }))]) - ]) - ] -}) -export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewInit { - // *** Toolbar *** - /** - * @internal - */ - @ContentChild(ToolbarDirective) externalToolbar: ToolbarDirective; - /** - * @internal - */ - @ContentChild(ToolbarAdditionalButtonsDirective) externalToolbarAdditionalButtons: ToolbarAdditionalButtonsDirective; - /** - * @internal - */ - @ContentChild(ToolbarAdditionalPanelButtonsDirective) externalToolbarAdditionalPanelButtons: ToolbarAdditionalPanelButtonsDirective; - /** - * @internal - */ - @ContentChild(AdditionalPanelsDirective) externalAdditionalPanels: AdditionalPanelsDirective; - - // *** Panels *** - - /** - * @internal - */ - @ContentChild(PanelDirective) externalPanel: PanelDirective; - /** - * @internal - */ - @ContentChild(ChatPanelDirective) externalChatPanel: ChatPanelDirective; - /** - * @internal - */ - @ContentChild(ActivitiesPanelDirective) externalActivitiesPanel: ActivitiesPanelDirective; - - /** - * @internal - */ - @ContentChild(ParticipantsPanelDirective) externalParticipantsPanel: ParticipantsPanelDirective; - /** - * @internal - */ - @ContentChild(ParticipantPanelItemDirective) externalParticipantPanelItem: ParticipantPanelItemDirective; - /** - * @internal - */ - @ContentChild(ParticipantPanelItemElementsDirective) externalParticipantPanelItemElements: ParticipantPanelItemElementsDirective; - - // *** Layout *** - /** - * @internal - */ - @ContentChild(LayoutDirective) externalLayout: LayoutDirective; - /** - * @internal - */ - @ContentChild(StreamDirective) externalStream: StreamDirective; - - /** - * @internal - */ - @ViewChild('defaultToolbar', { static: false, read: TemplateRef }) defaultToolbarTemplate: TemplateRef; - /** - * @internal - */ - @ViewChild('defaultPanel', { static: false, read: TemplateRef }) defaultPanelTemplate: TemplateRef; - /** - * @internal - */ - @ViewChild('defaultChatPanel', { static: false, read: TemplateRef }) defaultChatPanelTemplate: TemplateRef; - /** - * @internal - */ - @ViewChild('defaultParticipantsPanel', { static: false, read: TemplateRef }) defaultParticipantsPanelTemplate: TemplateRef; - /** - * @internal - */ - @ViewChild('defaultActivitiesPanel', { static: false, read: TemplateRef }) - defaultActivitiesPanelTemplate: TemplateRef; - - /** - * @internal - */ - @ViewChild('defaultParticipantPanelItem', { static: false, read: TemplateRef }) defaultParticipantPanelItemTemplate: TemplateRef; - /** - * @internal - */ - @ViewChild('defaultLayout', { static: false, read: TemplateRef }) defaultLayoutTemplate: TemplateRef; - /** - * @internal - */ - @ViewChild('defaultStream', { static: false, read: TemplateRef }) defaultStreamTemplate: TemplateRef; - - /** - * @internal - */ - openviduAngularToolbarTemplate: TemplateRef; - /** - * @internal - */ - openviduAngularToolbarAdditionalButtonsTemplate: TemplateRef; - /** - * @internal - */ - openviduAngularActivitiesPanelTemplate: TemplateRef; - - /** - * @internal - */ - openviduAngularToolbarAdditionalPanelButtonsTemplate: TemplateRef; - /** - * @internal - */ - openviduAngularPanelTemplate: TemplateRef; - /** - * @internal - */ - openviduAngularChatPanelTemplate: TemplateRef; - /** - * @internal - */ - openviduAngularParticipantsPanelTemplate: TemplateRef; - /** - * @internal - */ - openviduAngularAdditionalPanelsTemplate: TemplateRef; - /** - * @internal - */ - openviduAngularParticipantPanelItemTemplate: TemplateRef; - /** - * @internal - */ - openviduAngularParticipantPanelItemElementsTemplate: TemplateRef; - /** - * @internal - */ - openviduAngularLayoutTemplate: TemplateRef; - /** - * @internal - */ - openviduAngularStreamTemplate: TemplateRef; - - /** - * Tokens parameter is required to grant a participant access to a Session. - * This OpenVidu token will be use by each participant when connecting to a Session. - * - * This input accepts a {@link TokenModel} object type. - * - * @param {TokenModel} tokens The tokens parameter must be a {@link TokenModel} object. - * - */ - @Input() - set tokens(tokens: TokenModel) { - let openviduEdition; - if (!tokens || !tokens.webcam) { - this.log.e('No tokens received'); - return; - } - - try { - openviduEdition = new URL(tokens.webcam).searchParams.get('edition'); - } catch (error) { - this.log.e('Token received does not seem to be valid: ', tokens.webcam); - return; - } - - this.log.d('Tokens received'); - if (!!openviduEdition) { - this.openviduService.setOpenViduEdition(OpenViduEdition.PRO); - } else { - this.openviduService.setOpenViduEdition(OpenViduEdition.CE); - } - - this.openviduService.setWebcamToken(tokens.webcam); - if (tokens.screen) { - this.openviduService.setScreenToken(tokens.screen); - } else { - this.log.w('No screen token found. Screenshare feature will be disabled'); - } - - this.start(); - } - - /** - * Provides event notifications that fire when join button (in prejoin page) has been clicked. - */ - @Output() onJoinButtonClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when leave button has been clicked. - */ - @Output() onToolbarLeaveButtonClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when camera toolbar button has been clicked. - */ - @Output() onToolbarCameraButtonClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when microphone toolbar button has been clicked. - */ - @Output() onToolbarMicrophoneButtonClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when screenshare toolbar button has been clicked. - */ - @Output() onToolbarScreenshareButtonClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when fullscreen toolbar button has been clicked. - */ - @Output() onToolbarFullscreenButtonClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when participants panel button has been clicked. - */ - @Output() onToolbarParticipantsPanelButtonClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when chat panel button has been clicked. - */ - @Output() onToolbarChatPanelButtonClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when activities panel button has been clicked. - */ - @Output() onToolbarActivitiesPanelButtonClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when start recording button is clicked {@link ToolbarComponent}. - * The recording should be stopped using the REST API. - */ - @Output() onToolbarStartRecordingClicked: EventEmitter = new EventEmitter(); - /** - * Provides event notifications that fire when stop recording button is clicked from {@link ToolbarComponent}. - * The recording should be stopped using the REST API. - */ - @Output() onToolbarStopRecordingClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when start recording button is clicked {@link ActivitiesPanelComponent}. - * The recording should be stopped using the REST API. - */ - @Output() onActivitiesPanelStartRecordingClicked: EventEmitter = new EventEmitter(); - /** - * Provides event notifications that fire when stop recording button is clicked from {@link ActivitiesPanelComponent}. - * The recording should be stopped using the REST API. - */ - @Output() onActivitiesPanelStopRecordingClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when delete recording button is clicked from {@link ActivitiesPanelComponent}. - * The recording should be deleted using the REST API. - */ - @Output() onActivitiesPanelDeleteRecordingClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when a participant needs update the recordings information - * (usually when recording is stopped by the session moderator or recording panel is opened) from {@link ActivitiesPanelComponent}. - * The recordings should be updated using the REST API. - */ - @Output() onActivitiesPanelForceRecordingUpdate: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when play recording button is clicked from {@link ActivitiesPanelComponent}. - */ - @Output() onActivitiesPanelPlayRecordingClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when start broadcasting button is clicked from {@link ActivitiesPanelComponent}. - */ - @Output() onActivitiesPanelStartBroadcastingClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when start broadcasting button is clicked from {@link ActivitiesPanelComponent}. - */ - @Output() onActivitiesPanelStopBroadcastingClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when start broadcasting button is clicked from {@link ToolbarComponent}. - */ - @Output() onToolbarStartBroadcastingClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when start broadcasting button is clicked from {@link ToolbarComponent}. - */ - @Output() onToolbarStopBroadcastingClicked: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when OpenVidu Session is created. - * See {@link https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Session.html openvidu-browser Session}. - */ - @Output() onSessionCreated: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when local participant is created. - */ - @Output() onParticipantCreated: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire in the case of a node crash in your OpenVidu deployment. - * OpenVidu delegates the recovery of the sessions to the application in the event of a node crash. - * See {@link https://docs.openvidu.io/en/stable/openvidu-pro/fault-tolerance/ OpenVidu Pro Fault tolerance}. - */ - @Output() onNodeCrashed: EventEmitter = new EventEmitter(); - - /** - * Provides event notifications that fire when the application language has changed. - */ - @Output() onLangChanged: EventEmitter = new EventEmitter(); - - /** - * @internal - */ - showVideoconference: boolean = false; - /** - * @internal - */ - participantReady: boolean = false; - - /** - * @internal - */ - error: boolean = false; - /** - * @internal - */ - errorMessage: string = ''; - /** - * @internal - */ - showPrejoin: boolean = true; - - /** - * @internal - */ - isSessionInitialized: boolean = false; - - /** - * @internal - */ - loading = true; - private nodeCrashed: boolean = false; - private externalParticipantName: string; - private prejoinSub: Subscription; - private participantNameSub: Subscription; - private langSub: Subscription; - private log: ILogger; - - /** - * @internal - */ - constructor( - private loggerSrv: LoggerService, - private storageSrv: StorageService, - private participantService: ParticipantService, - private deviceSrv: DeviceService, - private openviduService: OpenViduService, - private actionService: ActionService, - private libService: OpenViduAngularConfigService, - private translateService: TranslateService - ) { - this.log = this.loggerSrv.get('VideoconferenceComponent'); - } - - async ngOnInit() { - this.subscribeToVideconferenceDirectives(); - } - - async ngOnDestroy() { - if (this.prejoinSub) this.prejoinSub.unsubscribe(); - if (this.participantNameSub) this.participantNameSub.unsubscribe(); - if (this.langSub) this.langSub.unsubscribe(); - this.deviceSrv.clear(); - await this.openviduService.clear(); - } - - /** - * @internal - */ - ngAfterViewInit() { - if (this.externalToolbar) { - this.log.d('Setting EXTERNAL TOOLBAR'); - this.openviduAngularToolbarTemplate = this.externalToolbar.template; - } else { - this.log.d('Setting DEFAULT TOOLBAR'); - if (this.externalToolbarAdditionalButtons) { - this.log.d('Setting EXTERNAL TOOLBAR ADDITIONAL BUTTONS'); - this.openviduAngularToolbarAdditionalButtonsTemplate = this.externalToolbarAdditionalButtons.template; - } - if (this.externalToolbarAdditionalPanelButtons) { - this.log.d('Setting EXTERNAL TOOLBAR ADDITIONAL PANEL BUTTONS'); - this.openviduAngularToolbarAdditionalPanelButtonsTemplate = this.externalToolbarAdditionalPanelButtons.template; - } - this.openviduAngularToolbarTemplate = this.defaultToolbarTemplate; - } - - if (this.externalPanel) { - this.log.d('Setting EXTERNAL PANEL'); - this.openviduAngularPanelTemplate = this.externalPanel.template; - } else { - this.log.d('Setting DEFAULT PANEL'); - - if (this.externalParticipantsPanel) { - this.openviduAngularParticipantsPanelTemplate = this.externalParticipantsPanel.template; - this.log.d('Setting EXTERNAL PARTICIPANTS PANEL'); - } else { - this.log.d('Setting DEFAULT PARTICIPANTS PANEL'); - if (this.externalParticipantPanelItem) { - this.log.d('Setting EXTERNAL P ITEM'); - this.openviduAngularParticipantPanelItemTemplate = this.externalParticipantPanelItem.template; - } else { - if (this.externalParticipantPanelItemElements) { - this.log.d('Setting EXTERNAL PARTICIPANT PANEL ITEM ELEMENT'); - this.openviduAngularParticipantPanelItemElementsTemplate = this.externalParticipantPanelItemElements.template; - } - this.openviduAngularParticipantPanelItemTemplate = this.defaultParticipantPanelItemTemplate; - this.log.d('Setting DEFAULT P ITEM'); - } - this.openviduAngularParticipantsPanelTemplate = this.defaultParticipantsPanelTemplate; - } - - if (this.externalChatPanel) { - this.log.d('Setting EXTERNAL CHAT PANEL'); - this.openviduAngularChatPanelTemplate = this.externalChatPanel.template; - } else { - this.log.d('Setting DEFAULT CHAT PANEL'); - this.openviduAngularChatPanelTemplate = this.defaultChatPanelTemplate; - } - - if (this.externalActivitiesPanel) { - this.log.d('Setting EXTERNAL ACTIVITIES PANEL'); - this.openviduAngularActivitiesPanelTemplate = this.externalActivitiesPanel.template; - } else { - this.log.d('Setting DEFAULT ACTIVITIES PANEL'); - this.openviduAngularActivitiesPanelTemplate = this.defaultActivitiesPanelTemplate; - } - - if (this.externalAdditionalPanels) { - this.log.d('Setting EXTERNAL ADDITIONAL PANELS'); - this.openviduAngularAdditionalPanelsTemplate = this.externalAdditionalPanels.template; - } - this.openviduAngularPanelTemplate = this.defaultPanelTemplate; - } - - if (this.externalLayout) { - this.log.d('Setting EXTERNAL LAYOUT'); - this.openviduAngularLayoutTemplate = this.externalLayout.template; - } else { - this.log.d('Setting DEAFULT LAYOUT'); - - if (this.externalStream) { - this.log.d('Setting EXTERNAL STREAM'); - this.openviduAngularStreamTemplate = this.externalStream.template; - } else { - this.log.d('Setting DEFAULT STREAM'); - this.openviduAngularStreamTemplate = this.defaultStreamTemplate; - } - this.openviduAngularLayoutTemplate = this.defaultLayoutTemplate; - } - } - - private async start() { - await this.deviceSrv.forceInitDevices(); - const nickname = this.externalParticipantName || this.storageSrv.getNickname() || `OpenVidu_User${Math.floor(Math.random() * 100)}`; - this.participantService.initLocalParticipant({ local: true, nickname }); - this.openviduService.initialize(); - if (this.deviceSrv.hasVideoDeviceAvailable() || this.deviceSrv.hasAudioDeviceAvailable()) { - await this.initwebcamPublisher(); - } - this.isSessionInitialized = true; - this.onSessionCreated.emit(this.openviduService.getWebcamSession()); - this.onParticipantCreated.emit(this.participantService.getLocalParticipant()); - this.loading = false; - this.participantReady = true; - if (this.nodeCrashed) { - this.nodeCrashed = false; - this.actionService.closeDialog(); - } - } - - private async initwebcamPublisher(): Promise { - return new Promise(async (resolve, reject) => { - try { - const pp = { - resolution: this.libService.getStreamResolution(), - frameRate: this.libService.getStreamFrameRate(), - videoSimulcast: this.libService.isSimulcastEnabled() - }; - const publisher = await this.openviduService.initDefaultPublisher(pp); - - if (publisher) { - publisher.once('accessDenied', async (e: any) => { - await this.handlePublisherError(e); - resolve(); - }); - publisher.once('accessAllowed', () => { - this.participantService.setMyCameraPublisher(publisher); - this.participantService.updateLocalParticipant(); - resolve(); - }); - } else { - this.participantService.setMyCameraPublisher(undefined); - this.participantService.updateLocalParticipant(); - } - } catch (error) { - this.actionService.openDialog(error.name.replace(/_/g, ' '), error.message, true); - this.log.e(error); - reject(); - } - }); - } - - /** - * @internal - */ - _onJoinButtonClicked() { - this.showVideoconference = true; - this.showPrejoin = false; - this.onJoinButtonClicked.emit(); - } - /** - * @internal - */ - onLeaveButtonClicked() { - this.showVideoconference = false; - this.participantReady = false; - this.onToolbarLeaveButtonClicked.emit(); - } - /** - * @internal - */ - onCameraButtonClicked() { - this.onToolbarCameraButtonClicked.emit(); - } - /** - * @internal - */ - onMicrophoneButtonClicked() { - this.onToolbarMicrophoneButtonClicked.emit(); - } - /** - * @internal - */ - onScreenshareButtonClicked() { - this.onToolbarScreenshareButtonClicked.emit(); - } - /** - * @internal - */ - onFullscreenButtonClicked() { - this.onToolbarFullscreenButtonClicked.emit(); - } - /** - * @internal - */ - onParticipantsPanelButtonClicked() { - this.onToolbarParticipantsPanelButtonClicked.emit(); - } - /** - * @internal - */ - onChatPanelButtonClicked() { - this.onToolbarChatPanelButtonClicked.emit(); - } - /** - * @internal - */ - onActivitiesPanelButtonClicked() { - this.onToolbarActivitiesPanelButtonClicked.emit(); - } - - /** - * @internal - */ - onStartRecordingClicked(from: string) { - if (from === 'toolbar') { - this.onToolbarStartRecordingClicked.emit(); - } else if (from === 'panel') { - this.onActivitiesPanelStartRecordingClicked.emit(); - } - } - - /** - * @internal - */ - onStopRecordingClicked(from: string) { - if (from === 'toolbar') { - this.onToolbarStopRecordingClicked.emit(); - } else if (from === 'panel') { - this.onActivitiesPanelStopRecordingClicked.emit(); - } - } - - /** - * @internal - */ - onDeleteRecordingClicked(recordingId: string) { - this.onActivitiesPanelDeleteRecordingClicked.emit(recordingId); - } - - /** - * @internal - */ - - onForceRecordingUpdate() { - this.onActivitiesPanelForceRecordingUpdate.emit(); - } - - /** - * @internal - */ - onStartBroadcastingClicked(broadcastUrl: string) { - // if (from === 'toolbar') { - // this.onToolbarStartRecordingClicked.emit(); - // } else if (from === 'panel') { - this.onActivitiesPanelStartBroadcastingClicked.emit(broadcastUrl); - // } - } - - /** - * @internal - */ - onStopBroadcastingClicked(from: string) { - if (from === 'toolbar') { - this.onToolbarStopBroadcastingClicked.emit(); - } else if (from === 'panel') { - this.onActivitiesPanelStopBroadcastingClicked.emit(); - } - } - - /** - * @internal - */ - _onSessionCreated(session: Session) { - this.onSessionCreated.emit(session); - } - - /** - * @internal - */ - _onNodeCrashed() { - this.nodeCrashed = true; - this.onNodeCrashed.emit(); - } - - private async handlePublisherError(e: any): Promise { - let message: string = ''; - if (e.name === OpenViduErrorName.DEVICE_ALREADY_IN_USE) { - this.log.w('Video device already in use. Disabling video device...'); - // Disabling video device - // Allow access to the room with only mic - this.deviceSrv.disableVideoDevices(); - return await this.initwebcamPublisher(); - } - if (e.name === OpenViduErrorName.NO_INPUT_SOURCE_SET) { - message = this.translateService.translate('ERRORS.DEVICE_NOT_FOUND'); - } - this.actionService.openDialog(e.name.replace(/_/g, ' '), message, true); - this.log.e(e.message); - } - - private subscribeToVideconferenceDirectives() { - this.prejoinSub = this.libService.prejoin.subscribe((value: boolean) => { - this.showPrejoin = value; - // this.cd.markForCheck(); - }); - - this.participantNameSub = this.libService.participantName.subscribe((nickname: string) => { - this.externalParticipantName = nickname; - }); - - this.langSub = this.translateService.langSelectedObs.subscribe((lang: LangOption) => { - this.onLangChanged.emit(lang); - }); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/config/custom-cdk-overlay.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/config/custom-cdk-overlay.ts deleted file mode 100644 index 956bafe5..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/config/custom-cdk-overlay.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { OverlayContainer } from '@angular/cdk/overlay'; -import { Injectable } from '@angular/core'; - -@Injectable() -export class CdkOverlayContainer extends OverlayContainer { - containerSelector = '.sidenav-main'; - customClass = 'cdk-overlay-container'; - protected _createContainer(): void { - const container = document.createElement('div'); - container.classList.add(this.customClass); - let element = this.getElement(this.containerSelector); - if (!element) { - element = this.getElement('body'); - } - this._containerElement = element.appendChild(container); - } - - setSelector(selector: string) { - const overlayElement = this.getElement('.' + this.customClass); - - if (overlayElement && this.containerSelector !== selector) { - const newContainerOverlayContainer = this.getElement(selector); - this.containerSelector = selector; - newContainerOverlayContainer?.appendChild(overlayElement); - } - } - private getElement(selector: string): Element { - return document.querySelector(selector); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/config/custom-flexlayout-breakpoints.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/config/custom-flexlayout-breakpoints.ts deleted file mode 100644 index ad2a02ab..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/config/custom-flexlayout-breakpoints.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { BREAKPOINT, LayoutDirective } from '@angular/flex-layout'; -import { Directive } from '@angular/core'; -import { } from '@angular/flex-layout'; - - -const LANDSCAPE_BREAKPOINTS = [ - { - alias: 'landscape', - suffix: 'Landscape', - mediaQuery: 'screen and (orientation: landscape)', - overlapping: false, - priority: 2001 - } -]; - -export const CustomBreakPointsProvider = { - provide: BREAKPOINT, - useValue: LANDSCAPE_BREAKPOINTS, - multi: true -}; - - -const selector = `[fxLayout.landscape]`; -const inputs = ['fxLayout.landscape']; - -@Directive({ selector, inputs }) -export class CustomLayoutExtensionDirective extends LayoutDirective { - protected inputs = inputs; -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/config/openvidu-angular.config.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/config/openvidu-angular.config.ts deleted file mode 100644 index a8f95a74..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/config/openvidu-angular.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ParticipantProperties, StreamModel } from '../models/participant.model'; - -export interface OpenViduAngularConfig { - production?: boolean, - participantFactory?: ParticipantFactoryFunction, -} - -export type ParticipantFactoryFunction = (props: ParticipantProperties, streamModel: StreamModel) => any; \ No newline at end of file diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/broadcasting-activity.directive.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/broadcasting-activity.directive.ts deleted file mode 100644 index 3c23f844..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/broadcasting-activity.directive.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { AfterViewInit, Directive, ElementRef, Input, OnDestroy } from '@angular/core'; -import { BroadcastingError } from '../../models/broadcasting.model'; -import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; - -/** - * The **broadcastingError** directive allows to show any possible error with the broadcasting in the {@link BroadcastingActivityComponent}. - * - * Default: `undefined` - * - * Type: {@link BroadcastingError} - * - * It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `broadcastingActivity` component: - * - * @example - * - * - * \ - * And it also can be used in the {@link BroadcastingActivityComponent}. - * @example - * - */ -@Directive({ - selector: 'ov-videoconference[broadcastingActivityBroadcastingError], ov-broadcasting-activity[broadcastingError]' -}) -export class BroadcastingActivityBroadcastingErrorDirective implements AfterViewInit, OnDestroy { - @Input() set broadcastingActivityBroadcastingError(value: BroadcastingError) { - this.broadcastingErrorValue = value; - this.update(this.broadcastingErrorValue); - } - @Input() set broadcastingError(value: BroadcastingError) { - this.broadcastingErrorValue = value; - this.update(this.broadcastingErrorValue); - } - - broadcastingErrorValue: BroadcastingError | undefined = undefined; - - constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} - - ngAfterViewInit() { - this.update(this.broadcastingErrorValue); - } - ngOnDestroy(): void { - this.clear(); - } - clear() { - this.broadcastingErrorValue = undefined; - this.update(undefined); - } - - update(value: BroadcastingError | undefined) { - if (this.libService.broadcastingError.getValue() !== value) { - this.libService.broadcastingError.next(value); - } - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/recording-activity.directive.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/recording-activity.directive.ts deleted file mode 100644 index 9c248c2c..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/recording-activity.directive.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { AfterViewInit, Directive, ElementRef, Input, OnDestroy } from '@angular/core'; -import { RecordingInfo } from '../../models/recording.model'; -import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; - -/** - * The **recordingsList** directive allows show the recordings available for the session in {@link RecordingActivityComponent}. - * - * Default: `[]` - * - * It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `recordingActivity` component: - * - * @example - * - * - * \ - * And it also can be used in the {@link RecordingActivityComponent}. - * @example - * - */ -@Directive({ - selector: 'ov-videoconference[recordingActivityRecordingsList], ov-recording-activity[recordingsList]' -}) -export class RecordingActivityRecordingsListDirective implements AfterViewInit, OnDestroy { - @Input() set recordingActivityRecordingsList(value: RecordingInfo[]) { - this.recordingsValue = value; - this.update(this.recordingsValue); - } - @Input() set recordingsList(value: RecordingInfo[]) { - this.recordingsValue = value; - this.update(this.recordingsValue); - } - - recordingsValue: RecordingInfo[] = []; - - constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} - - ngAfterViewInit() { - this.update(this.recordingsValue); - } - ngOnDestroy(): void { - this.clear(); - } - clear() { - this.recordingsValue = []; - this.update([]); - } - - update(value: RecordingInfo[]) { - if (this.libService.recordingsList.getValue() !== value) { - this.libService.recordingsList.next(value); - } - } -} - -/** - * The **recordingError** directive allows to show any possible error with the recording in the {@link RecordingActivityComponent}. - * - * Default: `[]` - * - * It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `recordingActivity` component: - * - * @example - * - * - * \ - * And it also can be used in the {@link RecordingActivityComponent}. - * @example - * - */ -@Directive({ - selector: 'ov-videoconference[recordingActivityRecordingError], ov-recording-activity[recordingError]' -}) -export class RecordingActivityRecordingErrorDirective implements AfterViewInit, OnDestroy { - @Input() set recordingActivityRecordingError(value: any) { - this.recordingErrorValue = value; - this.update(this.recordingErrorValue); - } - @Input() set recordingError(value: any) { - this.recordingErrorValue = value; - this.update(this.recordingErrorValue); - } - - recordingErrorValue: any = null; - - constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} - - ngAfterViewInit() { - this.update(this.recordingErrorValue); - } - ngOnDestroy(): void { - this.clear(); - } - clear() { - this.recordingErrorValue = null; - this.update(null); - } - - update(value: any) { - if (this.libService.recordingError.getValue() !== value) { - this.libService.recordingError.next(value); - } - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/stream.directive.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/stream.directive.ts deleted file mode 100644 index 51893254..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/stream.directive.ts +++ /dev/null @@ -1,255 +0,0 @@ -import { AfterViewInit, Directive, ElementRef, Input, OnDestroy } from '@angular/core'; -import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; - - -/** - * The **frameRate** directive allows initialize the publisher with a specific frame rate in stream component. - * - * Default: `30` - * - * It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `stream` component: - * - * @example - * - * - * \ - * And it also can be used in the {@link StreamComponent}. - * @example - * - */ -@Directive({ - selector: 'ov-videoconference[streamFrameRate], ov-stream[frameRate]' -}) -export class StreamFrameRateDirective implements AfterViewInit, OnDestroy { - @Input() set streamFrameRate(value: number) { - this._frameRate = value; - this.update(this._frameRate); - } - @Input() set frameRate(value: number) { - this._frameRate = value; - this.update(this._frameRate); - } - - _frameRate: number; - - constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} - - ngOnDestroy(): void { - this.clear(); - } - - ngAfterViewInit() { - this.update(this._frameRate); - } - - update(value: number) { - if (this.libService.streamFrameRate.getValue() !== value) { - this.libService.streamFrameRate.next(value); - } - } - - clear() { - this.update(30); - } -} - -/** - * The **resolution** directive allows to set a specific participant resolution in stream component. - * - * Default: `640x480` - * - * It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `stream` component: - * - * @example - * - * - * \ - * And it also can be used in the {@link StreamComponent}. - * @example - * - */ -@Directive({ - selector: 'ov-videoconference[streamResolution], ov-stream[resolution]' -}) -export class StreamResolutionDirective implements AfterViewInit, OnDestroy { - @Input() set streamResolution(value: string) { - this._resolution = value; - this.update(this._resolution); - } - @Input() set resolution(value: string) { - this._resolution = value; - this.update(this._resolution); - } - - _resolution: string; - - constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} - - ngOnDestroy(): void { - this.clear(); - } - - ngAfterViewInit() { - this.update(this._resolution); - } - - update(value: string) { - if (this.libService.streamResolution.getValue() !== value) { - this.libService.streamResolution.next(value); - } - } - - clear() { - this.update('640x480'); - } -} -/** - * The **displayParticipantName** directive allows show/hide the participants name in stream component. - * - * Default: `true` - * - * It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `stream` component: - * - * @example - * - * - * \ - * And it also can be used in the {@link StreamComponent}. - * @example - * - */ -@Directive({ - selector: 'ov-videoconference[streamDisplayParticipantName], ov-stream[displayParticipantName]' -}) -export class StreamDisplayParticipantNameDirective implements AfterViewInit, OnDestroy { - @Input() set streamDisplayParticipantName(value: boolean) { - this.displayParticipantNameValue = value; - this.update(this.displayParticipantNameValue); - } - @Input() set displayParticipantName(value: boolean) { - this.displayParticipantNameValue = value; - this.update(this.displayParticipantNameValue); - } - - displayParticipantNameValue: boolean; - - constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} - - ngOnDestroy(): void { - this.clear(); - } - - ngAfterViewInit() { - this.update(this.displayParticipantNameValue); - } - - update(value: boolean) { - if (this.libService.displayParticipantName.getValue() !== value) { - this.libService.displayParticipantName.next(value); - } - } - - clear() { - this.update(true); - } -} - -/** - * The **displayAudioDetection** directive allows show/hide the participants audio detection in stream component. - * - * Default: `true` - * - * It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `stream` component: - * - * @example - * - * - * \ - * And it also can be used in the {@link StreamComponent}. - * @example - * - */ -@Directive({ - selector: 'ov-videoconference[streamDisplayAudioDetection], ov-stream[displayAudioDetection]' -}) -export class StreamDisplayAudioDetectionDirective implements AfterViewInit, OnDestroy { - @Input() set streamDisplayAudioDetection(value: boolean) { - this.displayAudioDetectionValue = value; - this.update(this.displayAudioDetectionValue); - } - @Input() set displayAudioDetection(value: boolean) { - this.displayAudioDetectionValue = value; - this.update(this.displayAudioDetectionValue); - } - - displayAudioDetectionValue: boolean; - - constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} - - ngAfterViewInit() { - this.update(this.displayAudioDetectionValue); - } - ngOnDestroy(): void { - this.clear(); - } - - update(value: boolean) { - if (this.libService.displayAudioDetection.getValue() !== value) { - this.libService.displayAudioDetection.next(value); - } - } - clear() { - this.update(true); - } -} - -/** - * The **settingsButton** directive allows show/hide the participants settings button in stream component. - * - * Default: `true` - * - * It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `stream` component: - * - * @example - * - * - * \ - * And it also can be used in the {@link StreamComponent}. - * @example - * - */ -@Directive({ - selector: 'ov-videoconference[streamSettingsButton], ov-stream[settingsButton]' -}) -export class StreamSettingsButtonDirective implements AfterViewInit, OnDestroy { - @Input() set streamSettingsButton(value: boolean) { - this.settingsValue = value; - this.update(this.settingsValue); - } - @Input() set settingsButton(value: boolean) { - this.settingsValue = value; - this.update(this.settingsValue); - } - - settingsValue: boolean; - - constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} - - ngAfterViewInit() { - this.update(this.settingsValue); - } - - ngOnDestroy(): void { - this.clear(); - } - - update(value: boolean) { - if (this.libService.streamSettingsButton.getValue() !== value) { - this.libService.streamSettingsButton.next(value); - } - } - - clear() { - this.update(true); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/template/openvidu-angular.directive.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/template/openvidu-angular.directive.ts deleted file mode 100644 index 57172f08..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/template/openvidu-angular.directive.ts +++ /dev/null @@ -1,781 +0,0 @@ -import { Directive, TemplateRef, ViewContainerRef } from '@angular/core'; - -/** - * The ***ovToolbar** directive allows to replace the default toolbar component with a custom one. - * In the example below we've replaced the default toolbar and added the **toggleAudio** and **toggleVideo** buttons. - * Here we are using the {@link OpenViduService} for publishing/unpublishing the audio and video. - * - * You can run the associated tutorial [here](https://docs.openvidu.io/en/stable/components/openvidu-custom-toolbar#running-this-tutorial). - * - *```html - * - *
- * - * - *
- *
- * ``` - * - * ```javascript - * export class ToolbarDirectiveComponent { - * - * sessionId = 'toolbar-directive-example'; - * tokens!: TokenModel; - * - * publishVideo = true; - * publishAudio = true; - * - * constructor(private httpClient: HttpClient, private participantService: ParticipantService) { } - * - * async ngOnInit() { - * this.tokens = { - * webcam: await this.getToken(), - * screen: await this.getToken() - * }; - * } - * - * toggleVideo() { - * this.publishVideo = !this.publishVideo; - * this.participantService.publishVideo(this.publishVideo); - * } - * - * toggleAudio() { - * this.publishAudio = !this.publishAudio; - * this.participantService.publishAudio(this.publishAudio); - * } - * - * async getToken(): Promise { - * // Returns an OpeVidu token - * } - * - * } - * ``` - * - */ -@Directive({ - selector: '[ovToolbar]' -}) -export class ToolbarDirective { - /** - * @ignore - */ - constructor(public template: TemplateRef, public viewContainer: ViewContainerRef) {} -} - -/** - * The ***ovToolbarAdditionalButtons** directive allows to add additional buttons to center buttons group. - * In the example below we've added the same buttons as the {@link ToolbarDirective}. - * Here we are using the {@link ParticipantService} to check the audio or video status. - * - * You can run the associated tutorial [here](https://docs.openvidu.io/en/stable/components/openvidu-toolbar-buttons#running-this-tutorial). - * - *```html - * - *
- * - * - *
- *
- * ``` - * - * ```javascript - * export class ToolbarAdditionalButtonsDirectiveComponent { - * - sessionId = "panel-directive-example"; - tokens!: TokenModel; - - - * sessionId = 'toolbar-additionalbtn-directive-example'; - * tokens!: TokenModel; - * - * constructor( - * private httpClient: HttpClient, - * private participantService: ParticipantService - * ) { } - * - * async ngOnInit() { - * this.tokens = { - * webcam: await this.getToken(), - * screen: await this.getToken() - * }; - * } - * - * toggleVideo() { - * const publishVideo = !this.participantService.isMyVideoActive(); - * this.participantService.publishVideo(publishVideo); - * } - * - * toggleAudio() { - * const publishAudio = !this.participantService.isMyAudioActive(); - * this.participantService.publishAudio(publishAudio); - * } - * - * async getToken(): Promise { - * // Returns an OpeVidu token - * } - * - * } - * ``` - */ -@Directive({ - selector: '[ovToolbarAdditionalButtons]' -}) -export class ToolbarAdditionalButtonsDirective { - /** - * @ignore - */ - constructor(public template: TemplateRef, public viewContainer: ViewContainerRef) {} -} - - -/** - * The ***ovToolbarAdditionalPanelButtons** directive allows to add additional **panel buttons** to the toolbar. - * In the example below we've added a simple button without any functionality. To learn how to toggle the panel check the {@link AdditionalPanelsDirective}. - * - * You can run the associated tutorial [here](https://docs.openvidu.io/en/stable/components/openvidu-toolbar-panel-buttons#running-this-tutorial). - * - *```html - * - *
- * - *
- *
- * ``` - * - * ```javascript - * export class ToolbarAdditionalPanelButtonsDirectiveComponent { - * - * sessionId = "toolbar-additionalPanelbtn"; - * tokens!: TokenModel; - * - * constructor(private httpClient: HttpClient) { } - * - * async ngOnInit() { - * this.tokens = { - * webcam: await this.getToken(), - * screen: await this.getToken(), - * }; - * } - * - * onButtonClicked() { - * alert('button clicked'); - * } - * - * async getToken(): Promise { - * // Returns an OpeVidu token - * } - * - * } - * ``` - */ -@Directive({ - selector: '[ovToolbarAdditionalPanelButtons]' -}) -export class ToolbarAdditionalPanelButtonsDirective { - /** - * @ignore - */ - constructor(public template: TemplateRef, public viewContainer: ViewContainerRef) {} -} - - -/** - * The ***ovPanel** directive allows to replace the default panels with your own custom panels. This directive also allows to insert elements - * tagged with the {@link ChatPanelDirective}, {@link ParticipantsPanelDirective} and {@link AdditionalPanelsDirective}. - * - * In the example below we replace the entire {@link PanelComponent} using the ***ovPanel** directive. Inside of it, we customize - * the {@link ParticipantsPanelComponent} and {@link ChatPanelcomponent} using their own directives. - * - * You can run the associated tutorial [here](https://docs.openvidu.io/en/stable/components/openvidu-custom-panels#running-this-tutorial). - * - *```html - * - * - *
This is my custom chat panel
- *
- * This is my custom participants panel - *
- *
- *
- * ``` - * - * ```javascript - * export class PanelDirectiveComponent { - * - * sessionId = "panel-directive-example"; - * tokens!: TokenModel; - * - * constructor(private httpClient: HttpClient) { } - * - * async ngOnInit() { - * this.tokens = { - * webcam: await this.getToken(), - * screen: await this.getToken(), - * }; - * } - * - * async getToken(): Promise { - * // Returns an OpeVidu token - * } - * - * } - * ``` - */ -@Directive({ - selector: '[ovPanel]' -}) -export class PanelDirective { - /** - * @ignore - */ - constructor(public template: TemplateRef, public viewContainer: ViewContainerRef) {} -} - - -/** - * The ***ovAdditionalPanels** directive allows to add more extra panels to the {@link PanelComponent}. In this example we add a new - * panel alongside the default ones. - * - * To mimic the toggling behavior of the default panels, we need to add a new button in the {@link ToolbarComponent} - * using the {@link ToolbarAdditionalPanelButtonsDirective}. - * - * You can run the associated tutorial [here](https://docs.openvidu.io/en/stable/components/openvidu-additional-panels#running-this-tutorial). - * - *```html - * - *
- * - * - *
- *
- *
- *

NEW PANEL

- *

This is my new additional panel

- *
- *
- *

NEW PANEL 2

- *

This is other new panel

- *
- *
- *
- * ``` - *
- * - * We need to subscribe to the {@link ../injectables/PanelService.html#panelOpenedObs panelOpenedObs} Observable to listen to the panel status and update our boolean variables - * (`showExternalPanel` and `showExternalPanel2`) in charge of showing or hiding them. - * - * ```javascript - * export class AdditionalPanelsDirectiveComponent implements OnInit { - * - * sessionId = "toolbar-additionalbtn-directive-example"; - * tokens!: TokenModel; - * - * showExternalPanel: boolean = false; - * showExternalPanel2: boolean = false; - * - * constructor( - * private httpClient: HttpClient, - * private panelService: PanelService - * ) { } - * - * async ngOnInit() { - * this.subscribeToPanelToggling(); - * this.tokens = { - * webcam: await this.getToken(), - * screen: await this.getToken(), - * }; - * } - * - * subscribeToPanelToggling() { - * this.panelService.panelOpenedObs.subscribe( - * (ev: { opened: boolean; type?: PanelType | string }) => { - * this.showExternalPanel = ev.opened && ev.type === "my-panel"; - * this.showExternalPanel2 = ev.opened && ev.type === "my-panel2"; - * } - * ); - * } - * - * toggleMyPanel(type: string) { - * this.panelService.togglePanel(type); - * } - * - * async getToken(): Promise { - * // Returns an OpeVidu token - * } - * - * } - * ``` - */ -@Directive({ - selector: '[ovAdditionalPanels]' -}) -export class AdditionalPanelsDirective { - /** - * @ignore - */ - constructor(public template: TemplateRef, public viewContainer: ViewContainerRef) {} -} - - -/** - * The ***ovChatPanel** directive allows to replace the default chat panel template with a custom one. - * In the example below we replace the chat template in a few lines of code. - * - * You can run the associated tutorial [here](https://docs.openvidu.io/en/stable/components/openvidu-custom-chat-panel#running-this-tutorial). - * - * ```html - * - *
- *

Chat

- *
- *
    - *
  • {{ msg }}
  • - *
- *
- * - * - *
- *
- *``` - *
- * - * As we need to get the openvidu-browser **[Session](https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Session.html)** - * object for sending messages to others, we can get it from the `onSessionCreated` event fired by the {@link VideoconferenceComponent} - * when the session has been created. - * - * Once we have the session created, we can use the - * [signal](https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Session.html#signal) method to send our messages. - * - * ```javascript - * export class ChatPanelDirectiveComponent { - * - * sessionId = "chat-panel-directive-example"; - * tokens!: TokenModel; - * - * session!: Session; - * messages: string[] = []; - * - * constructor(private httpClient: HttpClient) { } - * - * async ngOnInit() { - * this.tokens = { - * webcam: await this.getToken(), - * screen: await this.getToken(), - * }; - * } - * - * onSessionCreated(session: Session) { - * this.session = session; - * this.session.on(`signal:${Signal.CHAT}`, (event: any) => { - * const msg = JSON.parse(event.data).message; - * this.messages.push(msg); - * }); - * } - * - * send(message: string): void { - * const signalOptions: SignalOptions = { - * data: JSON.stringify({ message }), - * type: Signal.CHAT, - * to: undefined, - * }; - * this.session.signal(signalOptions); - * } - * - * async getToken(): Promise { - * // Returns an OpeVidu token - * } - * - * } - * ``` - * - */ -@Directive({ - selector: '[ovChatPanel]' -}) -export class ChatPanelDirective { - constructor(public template: TemplateRef, public viewContainer: ViewContainerRef) {} -} - -/** - * backgroundEffectsPanel does not provide any customization for now - * @internal - */ -@Directive({ - selector: '[ovBackgroundEffectsPanel]' -}) -export class BackgroundEffectsPanelDirective { - constructor(public template: TemplateRef, public viewContainer: ViewContainerRef) {} -} - - -/** - * The ***ovActivitiesPanel** directive allows to replace the default activities panel template with a custom one. - * In the example below we replace the activities template in a few lines of code. - * - * You can run the associated tutorial [here](https://docs.openvidu.io/en/stable/components/openvidu-custom-activities-panel#running-this-tutorial). - * - * ```html - * - *
- *

ACTIVITIES

- *
- * CUSTOM ACTIVITIES - *
- *
- *
- *``` - *
- * - * As we need to assign the OpenVidu Tokens to the {@link VideoconferenceComponent}, we request them on the ngOnInit Angular lifecycle hook. - * - * ```javascript - * export class AppComponent implements OnInit { - * - * sessionId = "activities-panel-directive-example"; - * tokens!: TokenModel; - * - * constructor(private httpClient: HttpClient) { } - * - * async ngOnInit() { - * this.tokens = { - * webcam: await this.getToken(), - * screen: await this.getToken(), - * }; - * } - * - * async getToken(): Promise { - * // Returns an OpeVidu token - * } - * - *} - * ``` - * - */ -@Directive({ - selector: '[ovActivitiesPanel]' -}) -export class ActivitiesPanelDirective { - constructor(public template: TemplateRef, public viewContainer: ViewContainerRef) {} -} - -/** - * The ***ovParticipantsPanel** directive allows to replace the default participants panel template with a custom one. - * In the example below we replace the participants template in a few lines of code. - * - * You can run the associated tutorial [here](https://docs.openvidu.io/en/stable/components/openvidu-custom-participants-panel#running-this-tutorial). - * - * ```html - * - *
- *
    - *
  • {{localParticipant.nickname}}
  • - *
- *
    - *
  • {{p.nickname}}
  • - *
- *
- *
- *``` - *
- * - * We need to get the participants in our Session, so we use the {@link ParticipantService} to subscribe to the required Observables. - * We'll get the local participant and the remote participants to update our custom participants panel on any change. - * - * ```javascript - * export class ParticipantsPanelDirectiveComponent implements OnInit, OnDestroy { - * - * sessionId = 'participants-panel-directive-example'; - * tokens!: TokenModel; - * - * localParticipant!: ParticipantAbstractModel; - * remoteParticipants!: ParticipantAbstractModel[]; - * localParticipantSubs!: Subscription; - * remoteParticipantsSubs!: Subscription; - * - * constructor(private httpClient: HttpClient, private participantService: ParticipantService) { } - * - * async ngOnInit() { - * this.tokens = { - * webcam: await this.getToken(), - * screen: await this.getToken() - * }; - * } - * - * ngOnDestroy() { - * this.localParticipantSubs.unsubscribe(); - * this.remoteParticipantsSubs.unsubscribe(); - * } - * - * subscribeToParticipants() { - * this.localParticipantSubs = this.participantService.localParticipantObs.subscribe((p) => { - * this.localParticipant = p; - * }); - * this.remoteParticipantsSubs = this.participantService.remoteParticipantsObs.subscribe((participants) => { - * this.remoteParticipants = participants; - * }); - * } - * - * async getToken(): Promise { - * // Returns an OpeVidu token - * } - * - * } - * ``` - * - */ -@Directive({ - selector: '[ovParticipantsPanel]' -}) -export class ParticipantsPanelDirective { - constructor(public template: TemplateRef, public viewContainer: ViewContainerRef) {} -} - - -/** - * The ***ovParticipantPanelItem** directive allows to replace the default participant panel item template in the {@link ParticipantsPanelComponent} with a custom one. - * - * With ***ovParticipantPanelItem** directive we can access the participant object from its context using the `let` keyword and referencing the `participant` - * variable: `*ovParticipantPanelItem="let participant"`. Now we can access the {@link ParticipantAbstractModel} object. - * - * You can run the associated tutorial [here](https://docs.openvidu.io/en/stable/components/openvidu-custom-participant-panel-item#running-this-tutorial). - * - * ```html - * - *
- *

{{ participant.nickname }}

- * - * - * - * - * - *
- *
- *``` - * - * ```javascript - * export class ParticipantPanelItemDirectiveComponent { - * - * sessionId = 'participants-panel-directive-example'; - * tokens!: TokenModel; - * - * constructor(private httpClient: HttpClient) { } - * - * async ngOnInit() { - * this.tokens = { - * webcam: await this.getToken(), - * screen: await this.getToken() - * }; - * } - * - * async getToken(): Promise { - * // Returns an OpeVidu token - * } - * - * } - * ``` - * - */ -@Directive({ - selector: '[ovParticipantPanelItem]' -}) -export class ParticipantPanelItemDirective { - constructor(public template: TemplateRef, public viewContainer: ViewContainerRef) {} -} - -/** - * The ***ovParticipantPanelItemElements** directive allows to add elements to the {@link ParticipantsPanelItemComponent}. - * In the example below we add a simple button to disconnect from the session. - * - * With ***ovParticipantPanelItemElements** directive we can access the participant object from its context using - * the `let` keyword and referencing the `participant` variable: `*ovParticipantPanelItem="let participant"`. - * Now we can access the {@link ParticipantAbstractModel} object and enable the button just for the local participant. - * - * You can run the associated tutorial [here](https://docs.openvidu.io/en/stable/components/openvidu-custom-participant-panel-item-element#running-this-tutorial). - * - * ```html - * - *
- * - *
- *
- *
Session disconnected
- *``` - * - * ```javascript - * export class ParticipantPanelItemElementsDirectiveComponent { - * - * sessionId = "participants-panel-directive-example"; - * tokens!: TokenModel; - * - * connected = true; - * - * constructor(private httpClient: HttpClient, private openviduService: OpenViduService) { } - * - * async ngOnInit() { - * this.tokens = { - * webcam: await this.getToken(), - * screen: await this.getToken(), - * }; - * } - * - * leaveSession() { - * this.openviduService.disconnect(); - * this.connected = false; - * } - * - * async getToken(): Promise { - * // Returns an OpeVidu token - * } - * - * } - * ``` - */ -@Directive({ - selector: '[ovParticipantPanelItemElements]' -}) -export class ParticipantPanelItemElementsDirective { - constructor(public template: TemplateRef, public viewContainer: ViewContainerRef) {} -} - - -/** - * The ***ovLayout** directive allows to replace the default session layout with a custom one. - * - * As the deafult {@link StreamComponent} needs the participant stream, and as the participants streams extraction is not trivial, - * openvidu-angular provides a {@link ParticipantStreamsPipe} for easy extraction of the stream of each participant. In the example - * below you can see that on the HTML template, as the last component of the `*ngFor` statements (`| streams`). - * - * You can run the associated tutorial [here](https://docs.openvidu.io/en/stable/components/openvidu-custom-layout#running-this-tutorial). - * - * ```html - * - *
- *
- *
- * - *
- *
- * - *
- *
- *
- *
- *``` - * - * We need to get the participants in our Session, so we use the {@link ParticipantService} to subscribe to the required Observables. - * We'll get the local participant and the remote participants to display their streams in our custom session layout. - * - * ```javascript - * export class LayoutDirectiveComponent implements OnInit, OnDestroy { - * - * sessionId = 'layout-directive-example'; - * tokens!: TokenModel; - * - * localParticipant!: ParticipantAbstractModel; - * remoteParticipants!: ParticipantAbstractModel[]; - * localParticipantSubs!: Subscription; - * remoteParticipantsSubs!: Subscription; - * - * constructor(private httpClient: HttpClient, private participantService: ParticipantService) { } - * - * async ngOnInit() { - * this.tokens = { - * webcam: await this.getToken(), - * screen: await this.getToken() - * }; - * } - * - * ngOnDestroy() { - * this.localParticipantSubs.unsubscribe(); - * this.remoteParticipantsSubs.unsubscribe(); - * } - * - * subscribeToParticipants() { - * this.localParticipantSubs = this.participantService.localParticipantObs.subscribe((p) => { - * this.localParticipant = p; - * }); - * - * this.remoteParticipantsSubs = this.participantService.remoteParticipantsObs.subscribe((participants) => { - * this.remoteParticipants = participants; - * }); - * } - * - * async getToken(): Promise { - * // Returns an OpeVidu token - * } - * - * } - * ``` - */ -@Directive({ - selector: '[ovLayout]' -}) -export class LayoutDirective { - constructor(public template: TemplateRef, public container: ViewContainerRef) {} -} - -/** - * The ***ovStream** directive allows to replace the default {@link StreamComponent} template injecting a custom one. - * In the example below we customize the participant's nickname position and styles, replacing the default stream component. - * - * With ***ovStream** directive we can access to the stream object from its context using the `let` keyword and - * referencing the `stream` variable: `*ovStream="let stream"`. Now we can access the {@link StreamModel} object. - * - * You can run the associated tutorial [here](https://docs.openvidu.io/en/stable/components/openvidu-custom-stream#running-this-tutorial). - * - * ```html - * - *
- * - *

{{ stream.participant.nickname }}

- *
- *
- * ``` - * - * ```javascript - * export class StreamDirectiveComponent { - * - * sessionId = 'toolbar-directive-example'; - * tokens!: TokenModel; - * - * constructor(private httpClient: HttpClient) { } - * - * async ngOnInit() { - * this.tokens = { - * webcam: await this.getToken(), - * screen: await this.getToken() - * }; - * } - * - * async getToken(): Promise { - * // Returns an OpeVidu token - * } - * - * } - * ``` - */ -@Directive({ - selector: '[ovStream]' -}) -export class StreamDirective { - constructor(public template: TemplateRef, public container: ViewContainerRef) {} -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/matchers/nickname.matcher.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/matchers/nickname.matcher.ts deleted file mode 100644 index 4a7ffb31..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/matchers/nickname.matcher.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { FormControl, FormGroupDirective, NgForm } from '@angular/forms'; -import { ErrorStateMatcher } from '@angular/material/core'; - -/** Error when invalid control is dirty, touched, or submitted. */ -/** - * @internal - */ -export class NicknameMatcher implements ErrorStateMatcher { - isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { - const isSubmitted = form && form.submitted; - return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted)); - } -} \ No newline at end of file diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/models/broadcasting.model.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/models/broadcasting.model.ts deleted file mode 100644 index cbc438f2..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/models/broadcasting.model.ts +++ /dev/null @@ -1,13 +0,0 @@ -export enum BroadcastingStatus { - STARTING = 'starting', - STARTED = 'started', - STOPPING = 'stopping', - STOPPED = 'stopped', - FAILED = 'failed' -} - -export interface BroadcastingError { - message: string; - // If broadcasting feature is available or not - broadcastAvailable: boolean; -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/models/icon.model.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/models/icon.model.ts deleted file mode 100644 index 87b40307..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/models/icon.model.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @internal - */ -export enum VideoSizeIcon { - BIG = 'zoom_in', - NORMAL = 'zoom_out' -} \ No newline at end of file diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/models/panel.model.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/models/panel.model.ts deleted file mode 100644 index e740a983..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/models/panel.model.ts +++ /dev/null @@ -1,22 +0,0 @@ -export enum PanelType { - CHAT = 'chat', - PARTICIPANTS = 'participants', - BACKGROUND_EFFECTS = 'background-effects', - ACTIVITIES = 'activities', - SETTINGS = 'settings' - -} - -export interface PanelEvent { - opened: boolean; - type?: PanelType | string; - expand?: string; - oldType?: PanelType | string; -} - -export enum PanelSettingsOptions { - GENERAL = 'general', - AUDIO = 'audio', - VIDEO = 'video', - CAPTIONS = 'captions' -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/models/participant.model.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/models/participant.model.ts deleted file mode 100644 index 618198ec..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/models/participant.model.ts +++ /dev/null @@ -1,391 +0,0 @@ -import { Publisher, StreamManager } from 'openvidu-browser-v2compatibility'; -import { VideoType } from './video-type.model'; - -/** - * @internal - */ -export enum OpenViduRole { - MODERATOR = 'MODERATOR', - PUBLISHER = 'PUBLISHER' -} - -export interface StreamModel { - /** - * Whether the stream is available or not - */ - connected: boolean; - /** - * The stream type.{@link VideoType} - */ - type: VideoType; - /** - * The streamManager object from openvidu-browser library.{@link https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/StreamManager.html} - */ - streamManager: StreamManager | undefined; - /** - * Whether the stream is enlarged or not - */ - videoEnlarged: boolean; - /** - * Unique identifier of the stream - */ - connectionId: string | undefined; - /** - * The participant object - */ - participant?: ParticipantAbstractModel; -} - -export interface ParticipantProperties { - /** - * Whether the participant is local or not - */ - local: boolean; - /** - * The participant nickname - */ - nickname: string; - /** - * Unique identifier of the participant - */ - id?: string; - /** - * The participant color profile - */ - colorProfile?: string; - /** - * Whether the participant is muted forcibly or not - */ - isMutedForcibly?: boolean; -} - -export abstract class ParticipantAbstractModel { - streams: Map = new Map(); - id: string; - local: boolean; - nickname: string; - colorProfile: string; - isMutedForcibly: boolean; - - constructor(props: ParticipantProperties, model?: StreamModel) { - this.id = props.id || Math.random().toString(32).replace('.','_'); - this.local = props.local; - this.nickname = props.nickname; - this.colorProfile = !!props.colorProfile ? props.colorProfile : `hsl(${Math.random() * 360}, 100%, 80%)`; - this.isMutedForcibly = typeof props.isMutedForcibly === 'boolean' ? props.isMutedForcibly : false; - let streamModel: StreamModel = { - connected: model ? model.connected : true, - type: model ? model.type : VideoType.CAMERA, - streamManager: model?.streamManager, - videoEnlarged: model ? model.videoEnlarged : false, - connectionId: model?.connectionId, - participant: this - }; - this.streams.set(streamModel.type, streamModel); - } - - /** - * @internal - */ - addConnection(streamModel: StreamModel) { - streamModel.participant = this; - this.streams.set(streamModel.type, streamModel); - } - - /** - * @internal - */ - hasAudioActive(): boolean { - const cameraConnection = this.getCameraConnection(); - const screenConnection = this.getScreenConnection(); - - if (cameraConnection.connected) { - return this.isCameraAudioActive(); - } else if (screenConnection.connected) { - return this.isScreenAudioActive(); - } - return false; - } - - /** - * @internal - */ - private isCameraAudioActive(): boolean { - const cameraConnection = this.getCameraConnection(); - if (cameraConnection?.connected) { - return cameraConnection.streamManager?.stream?.audioActive || false; - } - return false; - } - - /** - * @internal - */ - public isCameraVideoActive(): boolean { - const cameraConnection = this.getCameraConnection(); - return cameraConnection?.connected && cameraConnection?.streamManager?.stream?.videoActive; - } - - /** - * @internal - */ - isScreenAudioActive(): boolean { - const screenConnection = this.getScreenConnection(); - if (screenConnection?.connected) { - return screenConnection?.streamManager?.stream?.audioActive || false; - } - return false; - } - - /** - * @internal - */ - hasConnectionType(type: VideoType): boolean { - return this.streams.has(type); - } - - /** - * @internal - */ - public getCameraConnection(): StreamModel { - return this.streams.get(VideoType.CAMERA); - } - - /** - * @internal - */ - public getScreenConnection(): StreamModel { - return this.streams.get(VideoType.SCREEN); - } - - /** - * @internal - * @returns The participant active connection types - */ - getActiveConnectionTypes(): VideoType[] { - const activeTypes: VideoType[] = []; - if (this.isCameraActive()) activeTypes.push(VideoType.CAMERA); - if (this.isScreenActive()) activeTypes.push(VideoType.SCREEN); - - return activeTypes; - } - - /** - * @internal - */ - setCameraConnectionId(connectionId: string) { - this.getCameraConnection().connectionId = connectionId; - } - - /** - * @internal - */ - setScreenConnectionId(connectionId: string) { - this.getScreenConnection().connectionId = connectionId; - } - - /** - * @internal - */ - removeConnection(connectionId: string): StreamModel { - const removeStream = this.getConnectionById(connectionId); - this.streams.delete(removeStream.type); - return removeStream; - } - - /** - * @internal - */ - hasConnectionId(connectionId: string): boolean { - return Array.from(this.streams.values()).some((conn) => conn.connectionId === connectionId); - } - - /** - * @internal - */ - getConnectionById(connectionId: string): StreamModel { - return Array.from(this.streams.values()).find((conn) => conn.connectionId === connectionId); - } - - /** - * @internal - */ - getAvailableConnections(): StreamModel[] { - return Array.from(this.streams.values()).filter((conn) => conn.connected); - } - - /** - * @internal - */ - isLocal(): boolean { - return this.local; - } - - /** - * @internal - */ - setNickname(nickname: string) { - this.nickname = nickname; - } - - /** - * @internal - */ - getNickname() { - return this.nickname; - } - - /** - * @internal - */ - setCameraPublisher(publisher: Publisher | undefined) { - const cameraConnection = this.getCameraConnection(); - if (cameraConnection) cameraConnection.streamManager = publisher; - } - - /** - * @internal - */ - setScreenPublisher(publisher: Publisher) { - const screenConnection = this.getScreenConnection(); - if (screenConnection) screenConnection.streamManager = publisher; - } - - /** - * @internal - */ - setPublisher(connType: VideoType, publisher: StreamManager) { - const connection = this.streams.get(connType); - if (connection) { - connection.streamManager = publisher; - } - } - - /** - * @internal - */ - isCameraActive(): boolean { - return this.getCameraConnection()?.connected; - } - - /** - * @internal - */ - enableCamera() { - const cameraConnection = this.getCameraConnection(); - if (cameraConnection) cameraConnection.connected = true; - } - - /** - * @internal - */ - disableCamera() { - const cameraConnection = this.getCameraConnection(); - if (cameraConnection) cameraConnection.connected = false; - } - - /** - * @internal - */ - isScreenActive(): boolean { - return this.getScreenConnection()?.connected; - } - - /** - * @internal - */ - enableScreen() { - const screenConnection = this.getScreenConnection(); - if (screenConnection) screenConnection.connected = true; - } - - /** - * @internal - */ - disableScreen() { - const screenConnection = this.getScreenConnection(); - if (screenConnection) screenConnection.connected = false; - } - - /** - * @internal - * @returns true if both camera and screen are active - */ - hasCameraAndScreenActives(): boolean { - return this.isCameraActive() && this.isScreenActive(); - } - - /** - * @internal - * @returns true if only screen is active - */ - hasOnlyScreenActive(): boolean { - return this.isScreenActive() && !this.isCameraActive(); - } - - /** - * @internal - * @returns true if only camera is active - */ - hasOnlyCameraActive(): boolean { - return this.isCameraActive() && !this.isScreenActive(); - } - - /** - * @internal - */ - setAllVideoEnlarged(enlarged: boolean) { - this.streams.forEach((conn) => (conn.videoEnlarged = enlarged)); - } - - /** - * @internal - */ - setCameraEnlarged(enlarged: boolean) { - this.streams.get(VideoType.CAMERA).videoEnlarged = enlarged; - } - - /** - * @internal - */ - setScreenEnlarged(enlarged: boolean) { - this.streams.get(VideoType.SCREEN).videoEnlarged = enlarged; - } - - /** - * @internal - */ - toggleVideoEnlarged(connectionId: string) { - this.streams.forEach((conn) => { - if (conn.connectionId === connectionId) { - conn.videoEnlarged = !conn.videoEnlarged; - } - }); - } - - /** - * @internal - */ - someHasVideoEnlarged(): boolean { - return Array.from(this.streams.values()).some((conn) => conn.videoEnlarged); - } - - /** - * @internal - */ - setMutedForcibly(muted: boolean) { - this.isMutedForcibly = muted; - } - - /** - * @internal - */ - getRole(): OpenViduRole { - return this.streams.get(VideoType.CAMERA)?.streamManager?.stream?.connection?.role; - } -} - -/** - * @internal - */ -export class ParticipantModel extends ParticipantAbstractModel {} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/models/recording.model.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/models/recording.model.ts deleted file mode 100644 index 1ab997ff..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/models/recording.model.ts +++ /dev/null @@ -1,19 +0,0 @@ -export enum RecordingStatus { - STARTING = 'starting', - STARTED = 'started', - STOPPING = 'stopping', - STOPPED = 'stopped', - FAILED = 'failed', - READY = 'ready' -} - -export interface RecordingInfo { - status: RecordingStatus; - id?: string; - name?: string; - reason?: string; - createdAt?: number; - duration?: number; - size?: string; - url?: string; -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/models/signal.model.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/models/signal.model.ts deleted file mode 100644 index e27e1a53..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/models/signal.model.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @internal - */ -export enum Signal { - NICKNAME_CHANGED = 'nicknameChanged', - CHAT = 'chat', - RECORDING_DELETED = 'recordingDeleted', -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/models/token.model.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/models/token.model.ts deleted file mode 100644 index b758ffb6..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/models/token.model.ts +++ /dev/null @@ -1,13 +0,0 @@ - -/** - * - * TokenModel type must be contain a `webcam` property (it will be used by a standart participant). - * It also optionally can contain a `screen` property. - * - * If TokenModel contains the `screen` token, the participant will be able to share a screen and camera at the same time. - * Otherwise, the participant will not be able to share a screen. - */ -export interface TokenModel { - webcam: string; - screen?: string; -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/models/video-type.model.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/models/video-type.model.ts deleted file mode 100644 index 61a7669f..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/models/video-type.model.ts +++ /dev/null @@ -1,13 +0,0 @@ -export enum VideoType { - CAMERA = 'CAMERA', - SCREEN = 'SCREEN', - CUSTOM = 'CUSTOM' -} - -/** - * @internal - */ -export enum ScreenType { - WINDOW = 'window', - SCREEN = 'screen' -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/pipes/participant.pipe.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/pipes/participant.pipe.ts deleted file mode 100644 index f22fa267..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/pipes/participant.pipe.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Pipe, PipeTransform } from '@angular/core'; -import { ParticipantAbstractModel, StreamModel } from '../models/participant.model'; -import { TranslateService } from '../services/translate/translate.service'; - -@Pipe({ name: 'streams' }) -export class ParticipantStreamsPipe implements PipeTransform { - constructor() {} - - transform(participants: ParticipantAbstractModel[] | ParticipantAbstractModel): StreamModel[] { - let streams: StreamModel[] = []; - if(participants && Object.keys(participants).length > 0){ - if (Array.isArray(participants)) { - streams = participants.map(p => p.getAvailableConnections()).flat(); - } else { - streams = participants.getAvailableConnections(); - } - } - return streams; - } -} - -/** - * @internal - */ -@Pipe({ name: 'streamTypesEnabled' }) -export class StreamTypesEnabledPipe implements PipeTransform { - constructor(private translateService: TranslateService) {} - - transform(participant: ParticipantAbstractModel): string { - - const activeStreams = participant?.getActiveConnectionTypes() ?? []; - const streamNames = activeStreams.map(streamType => this.translateService.translate(`PANEL.PARTICIPANTS.${streamType}`)); - const streamsString = streamNames.join(', '); - - return `(${streamsString})`; - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/broadcasting/broadcasting.service.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/broadcasting/broadcasting.service.ts deleted file mode 100644 index 7f29860f..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/broadcasting/broadcasting.service.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Injectable } from '@angular/core'; -import { BehaviorSubject, Observable } from 'rxjs'; -import { BroadcastingStatus } from '../../models/broadcasting.model'; - -@Injectable({ - providedIn: 'root' -}) -export class BroadcastingService { - /** - * Broadcasting status Observable which pushes the broadcasting state in every update. - */ - broadcastingStatusObs: Observable<{ status: BroadcastingStatus; time?: Date } | undefined>; - - private broadcastingTime: Date | undefined; - private broadcastingTimeInterval: NodeJS.Timeout; - private broadcastingStatus = >new BehaviorSubject(undefined); - - /** - * @internal - */ - constructor() { - this.broadcastingStatusObs = this.broadcastingStatus.asObservable(); - } - - /** - * Update the broadcasting status. This method is used by the OpenVidu Angular library to update the broadcasting status. - * @param status {@link BroadcastingStatus} - */ - updateStatus(status: BroadcastingStatus) { - this.broadcastingStatus.next({ status, time: this.broadcastingTime }); - } - - startBroadcasting() { - this.startBroadcastingTime(); - this.updateStatus(BroadcastingStatus.STARTED); - } - - stopBroadcasting() { - this.stopBroadcastingTime(); - this.updateStatus(BroadcastingStatus.STOPPED); - } - - private startBroadcastingTime() { - this.broadcastingTime = new Date(); - this.broadcastingTime.setHours(0, 0, 0, 0); - this.broadcastingTimeInterval = setInterval(() => { - this.broadcastingTime?.setSeconds(this.broadcastingTime.getSeconds() + 1); - this.broadcastingTime = new Date(this.broadcastingTime.getTime()); - this.broadcastingStatus.next({ status: this.broadcastingStatus.getValue()?.status, time: this.broadcastingTime }); - }, 1000); - } - - private stopBroadcastingTime() { - clearInterval(this.broadcastingTimeInterval); - this.broadcastingTime = undefined; - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/chat/chat.service.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/chat/chat.service.ts deleted file mode 100644 index 7a151335..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/chat/chat.service.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs/internal/Observable'; -import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; - -import { ILogger } from '../../models/logger.model'; -import { ChatMessage } from '../../models/chat.model'; -import { INotificationOptions } from '../../models/notification-options.model'; - -import { ActionService } from '../action/action.service'; -import { OpenViduService } from '../openvidu/openvidu.service'; -import { LoggerService } from '../logger/logger.service'; -import { Signal } from '../../models/signal.model'; -import { PanelService } from '../panel/panel.service'; -import { ParticipantService } from '../participant/participant.service'; -import { PanelType } from '../../models/panel.model'; - -/** - * @internal - */ -@Injectable({ - providedIn: 'root' -}) -export class ChatService { - messagesObs: Observable; - private messageSound: HTMLAudioElement; - protected _messageList = >new BehaviorSubject([]); - protected messageList: ChatMessage[] = []; - protected log: ILogger; - constructor( - protected loggerSrv: LoggerService, - protected openviduService: OpenViduService, - protected participantService: ParticipantService, - protected panelService: PanelService, - protected actionService: ActionService - ) { - this.log = this.loggerSrv.get('ChatService'); - this.messagesObs = this._messageList.asObservable(); - this.messageSound = new Audio('data:audio/wav;base64,SUQzAwAAAAAAekNPTU0AAAAmAAAAAAAAAFJlY29yZGVkIG9uIDI3LjAxLjIwMjEgaW4gRWRpc29uLkNPTU0AAAAmAAAAWFhYAFJlY29yZGVkIG9uIDI3LjAxLjIwMjEgaW4gRWRpc29uLlRYWFgAAAAQAAAAU29mdHdhcmUARWRpc29u//uQxAAAAAAAAAAAAAAAAAAAAAAAWGluZwAAAA8AAAAJAAALNABMTExMTExMTExMTGxsbGxsbGxsbGxsiIiIiIiIiIiIiIijo6Ojo6Ojo6Ojo76+vr6+vr6+vr6+1NTU1NTU1NTU1NTk5OTk5OTk5OTk5PX19fX19fX19fX1//////////////8AAAA8TEFNRTMuMTAwBK8AAAAAAAAAABUgJAadQQABzAAACzQeSO05AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//vAxAAABsADb7QQACOOLW3/NaBQzcKNbIACRU4IPh+H1Bhx+D7xQH4IHBIcLh+D/KOk4PwQcUOfy7/5c/IQQdrP8p1g+/////4YmoaJIwUxAFESnqIkyedtyHBoIBBD8xRVFILBVXBA8OKGuWmpLAiIAgcMHgAiQM8uBWBHlMp9xxWyoxCaksudVh8KBx50YE0aK0syZbR704cguOpoXYAqcWGp2LDxF/YFSUFkYWDpfFqiICYsMX7nYBeBwqWVu/eWkW9sxXVlRstdTUjZp2R1qWXSSnooIdGHXZlVt/VA7kSkOMsgTHdzVqrds5Sqe3Kqamq8ytRR2V2unJ5+Ua5TV8qW5jlnW3u7DOvu5Z1a1rC5hWwzy1rD8KXWeW/y3hjrPLe61NvKVWix61qlzMpXARASAAS9weVYFrKBrMWqu6jjUZ7fTbfURVYa/M7yswHEFcSLKLxqmslA6BeR7roKj6JqOin0zpcOsgrR+x0kUiko0SNUDpLOuSprSMjVJNz6/rpOpNHRUlRNVImJq6lJPd3dE1b0ldExPFbgZMgYOwaBR942K9XsCn9m9lwgoQgAACZu3yILcRAQaUpwkvPr+a6+6KdVuq9gQIb1U7y4HjTa7HGscIisVOM5lXYFkWydyDBYmjp7oKgOUYUacqINdIqIEMd0FBAWiz/UyMqbMzMchf7XOtKFoSXM8QcfQaNlmA8HQ0tbXsD56lKDIvZ3XYxS3vulF0MAQQnvwnBXQfZPwLwVAMkYoSghSkIpckFJOBBNJZmYhE4E7P58SGQAgjVRZ1ZtNmo2rHq7nz3mS2U6OiXGtkhZehWmijBt/3d1TGcQEq42sxqOUFEQVDwWBY0tRsAioZKw6WJhg69O6pJra3XaSp791mB2IASQldhZLfOAk7DIgCXxTHo0nWBshqN0Y84zMGzCMKRtYGbVvz7WAVC5NzrmykQLIlrfN2qHXQ6Z/qUmDKX/+3DE1gAPtQ9b/YaAIeAiqv2GFazATncobkc9EAAkvb9pnMjVsk3wQhM9Llh+HCIRFERd4sLROgTPOK2jHfzHpU382nQFIAgACc2fGGBODtNkTQqhIzJHrH4NFkIEcw6PKxgocFSm3CrgiDYp1tMRSzjwCVaVDj43vWr7jWiC4oaHsHa27zUKxJKDNef/jXeGuxlKTY2dTwOFKA+y6l2TnRhImDKhYQgEia822x5Zt6y5b96ngYYjIBDeuCFQwnowEHFcp3F3Q2yFFZLvS54JdWCn+lVJXjs1V1u3qntRpyU8I7Uq3/ay03bW1ndLf92/uUxpELIO44f3Kr6CBbEYW5dOlWo5LKwRnMbRHsUId8KFVgUFXg+GEpWg9Vv41YxbN1tuymfD5Cr/3HMVUhALLdtDLQpBOv1r//tgxO0ADnD3V+ekTeG+G6q9hY30qMDU1SSNOegcyOxBoQ6FNCdLvxHr23ta0sU9ysR0WbGp8xM0j1rmy6Zr61vFbVi1920wDjexZD1Z+TpXaAnGC+1gfGlRYSgYUZSeasoiXkDdS8A7z2CJdo8X3+M5NAxThdP9vO5OpACoq7KA8i6CmgsiBNQ+BkMg9yVkFKk5DiSpPVTGZJJ0XCtvGs0fKYhJ1Sb+MYfbrmtaZw+f619TVxvG5msonGaUczjGdaJoY6OcuBcGi5RYaShYxh1TNgZGJNCzgoXIN4rdR1pV0JWhFmfyldXv/JcJhgBTctuDPFGOdFAmCeC4h9ncJKcguVzY//tgxPOADhDdUeelDSHXH6o9gwrUqD7gLVJhOFnCIov6lFMYCyLz5OtEnP0OCssoUOxoKYq1NRqMpI7E75LkV8jKdIyOCknQELjSQSIuahE0OjfSySUn1W63D7/HmEXCJq83cxwh1KJ2/AANk0J/F+vsgcl1QRtTY1iDZMF0eTtOv4KncRPWe0b0xGlbTjXSib4W3AlD5TypIs2e3aqryiyIkpcxOMeN3GtGH12uYqkWhO0dqSlA9aq6uwhmNp3cAAinFeOC6lmceR2EiGUjwM14WJE5cVj0Ss0zW1vY5ZjnpSSL3Fs/V2kvm3VN90v4Zn8/mlRoVZF07uiFRV3nb+Mxz9LI//tgxPgADjytT+w8beHFFen88ZuE0l3ZwRBEJJwAAVdxwnIVhoQyXVGAWpKYIQ8VhfxuratfsU5ID7+4IOeoYj0s3vrerQYt1oo8FPA5Yi/j+ig7Cprmx3iziji76xilapmKJEQCVJQABLYVBTxyRmsXMv5AC/C2TmwTQviGYc5ILASBakpWy1I4As5Z++CQmtb3UTMNv1opus8JJzSpx8Pgv8Ul6ktLZ3Oy7QEI6CIkVHyXmE+tt69/P0V0lIuLQGmhCSAQCJEiVa9VVEiSQV+QU26Kx/Tv/EGG5PBQoapMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//tQxP4ADRjPT+ekbSFXFqi9h5k0qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqv/7MMT8AAmom0HsMTJpKRLmPPYiUKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqv/7MMTzgElMmTfnpNJo1AimfPSZgaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqv/7EMTWA8AAAaQAAAAgAAA0gAAABKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'); - this.messageSound.volume = 0.6; - } - - subscribeToChat() { - const session = this.openviduService.getWebcamSession(); - session.on(`signal:${Signal.CHAT}`, (event: any) => { - const connectionId = event.from.connectionId; - const data = JSON.parse(event.data); - const isMyOwnConnection = this.openviduService.isMyOwnConnection(connectionId); - this.messageList.push({ - isLocal: isMyOwnConnection, - nickname: data.nickname, - message: data.message - }); - if (!this.panelService.isChatPanelOpened()) { - const notificationOptions: INotificationOptions = { - message: `${data.nickname.toUpperCase()} sent a message`, - cssClassName: 'messageSnackbar', - buttonActionText: 'READ' - }; - this.launchNotification(notificationOptions); - this.messageSound.play().catch(() => {}); - - } - this._messageList.next(this.messageList); - }); - } - - async sendMessage(message: string) { - message = message.replace(/ +(?= )/g, ''); - if (message !== '' && message !== ' ') { - const data = { - message: message, - nickname: this.participantService.getMyNickname() - }; - - await this.openviduService.sendSignal(Signal.CHAT, undefined, data); - } - } - - protected launchNotification(options: INotificationOptions) { - this.actionService.launchNotification(options, this.panelService.togglePanel.bind(this.panelService, PanelType.CHAT)); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/config/openvidu-angular.config.service.spec.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/config/openvidu-angular.config.service.spec.ts deleted file mode 100644 index 447a1f64..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/config/openvidu-angular.config.service.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { OpenViduAngularConfig } from '../../config/openvidu-angular.config'; - -import { OpenViduAngularConfigService } from './openvidu-angular.config.service'; - -describe('OpenViduAngularConfigService', () => { - let service: OpenViduAngularConfigService; - const config: OpenViduAngularConfig = { production: false }; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - OpenViduAngularConfigService, - {provide: 'LIB_CONFIG', useValue: config}] - }); - service = TestBed.inject(OpenViduAngularConfigService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/config/openvidu-angular.config.service.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/config/openvidu-angular.config.service.ts deleted file mode 100644 index 6398d218..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/config/openvidu-angular.config.service.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { Inject, Injectable } from '@angular/core'; -import { BehaviorSubject, Observable } from 'rxjs'; -import { OpenViduAngularConfig, ParticipantFactoryFunction } from '../../config/openvidu-angular.config'; -import { BroadcastingError } from '../../models/broadcasting.model'; -import { RecordingInfo } from '../../models/recording.model'; - -// import { version } from '../../../../package.json'; - -/** - * @internal - */ -@Injectable() -export class OpenViduAngularConfigService { - private configuration: OpenViduAngularConfig; - minimal = >new BehaviorSubject(false); - minimalObs: Observable; - participantName = >new BehaviorSubject(''); - participantNameObs: Observable; - prejoin = >new BehaviorSubject(true); - prejoinObs: Observable; - - simulcast = >new BehaviorSubject(false); - simulcastObs: Observable; - - videoMuted = >new BehaviorSubject(undefined); - videoMutedObs: Observable; - audioMuted = >new BehaviorSubject(undefined); - audioMutedObs: Observable; - screenshareButton = >new BehaviorSubject(true); - screenshareButtonObs: Observable; - - fullscreenButton = >new BehaviorSubject(true); - fullscreenButtonObs: Observable; - - captionsButton = >new BehaviorSubject(true); - captionsButtonObs: Observable; - - toolbarSettingsButton = >new BehaviorSubject(true); - toolbarSettingsButtonObs: Observable; - - leaveButton = >new BehaviorSubject(true); - leaveButtonObs: Observable; - - participantsPanelButton = >new BehaviorSubject(true); - participantsPanelButtonObs: Observable; - - chatPanelButton = >new BehaviorSubject(true); - chatPanelButtonObs: Observable; - - activitiesPanelButton = >new BehaviorSubject(true); - activitiesPanelButtonObs: Observable; - - displaySessionName = >new BehaviorSubject(true); - displaySessionNameObs: Observable; - - streamFrameRate = >new BehaviorSubject(30); - streamFrameRateObs: Observable; - - streamResolution = >new BehaviorSubject('640x480'); - streamResolutionObs: Observable; - displayLogo = >new BehaviorSubject(true); - displayLogoObs: Observable; - displayParticipantName = >new BehaviorSubject(true); - displayParticipantNameObs: Observable; - displayAudioDetection = >new BehaviorSubject(true); - displayAudioDetectionObs: Observable; - streamSettingsButton = >new BehaviorSubject(true); - streamSettingsButtonObs: Observable; - participantItemMuteButton = >new BehaviorSubject(true); - participantItemMuteButtonObs: Observable; - backgroundEffectsButton = >new BehaviorSubject(true); - backgroundEffectsButtonObs: Observable; - recordingsList: BehaviorSubject = new BehaviorSubject([]); - recordingsListObs: Observable; - recordingButton = >new BehaviorSubject(true); - recordingButtonObs: Observable; - broadcastingButton = >new BehaviorSubject(true); - broadcastingButtonObs: Observable; - recordingActivity = >new BehaviorSubject(true); - recordingActivityObs: Observable; - broadcastingActivity = >new BehaviorSubject(true); - broadcastingActivityObs: Observable; - recordingError = >new BehaviorSubject(null); - recordingErrorObs: Observable; - broadcastingErrorObs: Observable; - broadcastingError = >new BehaviorSubject(undefined); - // Admin - adminRecordingsList: BehaviorSubject = new BehaviorSubject([]); - adminRecordingsListObs: Observable; - adminLoginError = >new BehaviorSubject(null); - adminLoginErrorObs: Observable; - - constructor(@Inject('OPENVIDU_ANGULAR_CONFIG') config: OpenViduAngularConfig) { - this.configuration = config; - console.log(this.configuration); - if (this.isProduction()) console.log('OpenVidu Angular Production Mode'); - this.minimalObs = this.minimal.asObservable(); - this.participantNameObs = this.participantName.asObservable(); - this.prejoinObs = this.prejoin.asObservable(); - this.videoMutedObs = this.videoMuted.asObservable(); - this.audioMutedObs = this.audioMuted.asObservable(); - this.simulcastObs = this.simulcast.asObservable(); - //Toolbar observables - this.screenshareButtonObs = this.screenshareButton.asObservable(); - this.fullscreenButtonObs = this.fullscreenButton.asObservable(); - this.backgroundEffectsButtonObs = this.backgroundEffectsButton.asObservable(); - this.leaveButtonObs = this.leaveButton.asObservable(); - this.participantsPanelButtonObs = this.participantsPanelButton.asObservable(); - this.chatPanelButtonObs = this.chatPanelButton.asObservable(); - this.activitiesPanelButtonObs = this.activitiesPanelButton.asObservable(); - this.displaySessionNameObs = this.displaySessionName.asObservable(); - this.displayLogoObs = this.displayLogo.asObservable(); - this.recordingButtonObs = this.recordingButton.asObservable(); - this.broadcastingButtonObs = this.broadcastingButton.asObservable(); - this.toolbarSettingsButtonObs = this.toolbarSettingsButton.asObservable(); - this.captionsButtonObs = this.captionsButton.asObservable(); - //Stream observables - this.streamFrameRateObs = this.streamFrameRate.asObservable(); - this.streamResolutionObs = this.streamResolution.asObservable(); - this.displayParticipantNameObs = this.displayParticipantName.asObservable(); - this.displayAudioDetectionObs = this.displayAudioDetection.asObservable(); - this.streamSettingsButtonObs = this.streamSettingsButton.asObservable(); - // Participant item observables - this.participantItemMuteButtonObs = this.participantItemMuteButton.asObservable(); - // Recording activity observables - this.recordingActivityObs = this.recordingActivity.asObservable(); - this.recordingsListObs = this.recordingsList.asObservable(); - this.recordingErrorObs = this.recordingError.asObservable(); - // Broadcasting activity - this.broadcastingActivityObs = this.broadcastingActivity.asObservable(); - this.broadcastingErrorObs = this.broadcastingError.asObservable(); - // Admin dashboard - this.adminRecordingsListObs = this.adminRecordingsList.asObservable(); - this.adminLoginErrorObs = this.adminLoginError.asObservable(); - } - - getConfig(): OpenViduAngularConfig { - return this.configuration; - } - isProduction(): boolean { - return this.configuration?.production || false; - } - - hasParticipantFactory(): boolean { - return typeof this.getConfig().participantFactory === 'function'; - } - - getParticipantFactory(): ParticipantFactoryFunction { - return this.getConfig().participantFactory; - } - - isRecordingEnabled(): boolean { - return this.recordingButton.getValue() && this.recordingActivity.getValue(); - } - - isBroadcastingEnabled(): boolean { - return this.broadcastingButton.getValue() && this.broadcastingActivity.getValue(); - } - - getStreamResolution(): string { - return this.streamResolution.getValue(); - } - - getStreamFrameRate(): number { - return this.streamFrameRate.getValue(); - } - - isSimulcastEnabled(): boolean { - return this.simulcast.getValue() || false; - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/openvidu/openvidu.service.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/openvidu/openvidu.service.ts deleted file mode 100644 index 2318e0c9..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/openvidu/openvidu.service.ts +++ /dev/null @@ -1,663 +0,0 @@ -import { Injectable, Injector } from '@angular/core'; -import { - Connection, - OpenVidu, - OpenViduError, - OpenViduErrorName, - Publisher, - PublisherProperties, - Session, - SignalOptions, - Stream, - StreamManager -} from 'openvidu-browser-v2compatibility'; - -import { BehaviorSubject, Observable } from 'rxjs'; -import { CameraType } from '../../models/device.model'; -import { ILogger } from '../../models/logger.model'; -import { OpenViduEdition } from '../../models/openvidu.model'; -import { Signal } from '../../models/signal.model'; -import { ScreenType, VideoType } from '../../models/video-type.model'; -import { OpenViduAngularConfigService } from '../config/openvidu-angular.config.service'; -import { DeviceService } from '../device/device.service'; -import { LoggerService } from '../logger/logger.service'; -import { ParticipantService } from '../participant/participant.service'; -import { PlatformService } from '../platform/platform.service'; - -@Injectable({ - providedIn: 'root' -}) -export class OpenViduService { - /* - * @internal - */ - isSttReadyObs: Observable; - private ovEdition: OpenViduEdition; - private webcamToken = ''; - private screenToken = ''; - protected OV: OpenVidu; - protected OVScreen: OpenVidu; - protected webcamSession: Session; - protected screenSession: Session; - protected videoSource = undefined; - protected audioSource = undefined; - private STT_TIMEOUT_MS = 2 * 1000; - private sttReconnectionTimeout: NodeJS.Timeout; - private _isSttReady: BehaviorSubject = new BehaviorSubject(true); - protected log: ILogger; - - /** - * @internal - */ - constructor( - protected libService: OpenViduAngularConfigService, - protected platformService: PlatformService, - protected loggerSrv: LoggerService, - private injector: Injector, - protected deviceService: DeviceService - ) { - this.log = this.loggerSrv.get('OpenViduService'); - this.isSttReadyObs = this._isSttReady.asObservable(); - } - - /** - * @internal - */ - initialize() { - this.OV = new OpenVidu(); - this.OV.setAdvancedConfiguration({ - publisherSpeakingEventsOptions: { - interval: 50 - } - }); - if (this.libService.isProduction()) this.OV.enableProdMode(); - this.webcamSession = this.OV.initSession(); - - // Initialize screen session only if it is not mobile platform - if (!this.platformService.isMobile()) { - this.OVScreen = new OpenVidu(); - if (this.libService.isProduction()) this.OVScreen.enableProdMode(); - this.screenSession = this.OVScreen.initSession(); - } - } - - /** - * @internal - */ - setWebcamToken(token: string) { - this.webcamToken = token; - } - - /** - * @internal - */ - setScreenToken(token: string) { - this.screenToken = token; - } - - /** - * @internal - */ - getWebcamToken(): string { - return this.webcamToken; - } - - /** - * @internal - */ - getScreenToken(): string { - return this.screenToken; - } - - /** - * @internal - */ - isOpenViduCE(): boolean { - return this.ovEdition === OpenViduEdition.CE; - } - - /** - * @internal - */ - isOpenViduPro(): boolean { - return this.ovEdition === OpenViduEdition.PRO; - } - - /** - * @internal - */ - setOpenViduEdition(edition: OpenViduEdition) { - this.ovEdition = edition; - } - - isSessionConnected(): boolean { - return !!this.webcamSession.connection; - } - - /** - * @internal - */ - async clear() { - this.videoSource = undefined; - this.audioSource = undefined; - } - - /** - * - * Returns the local Session. See {@link https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Session.html Session} object. - */ - getSession(): Session { - return this.getWebcamSession(); - } - - /** - * @internal - */ - getWebcamSession(): Session { - return this.webcamSession; - } - - /** - * @internal - */ - isWebcamSessionConnected(): boolean { - return !!this.webcamSession.capabilities; - } - - /** - * @internal - */ - getScreenSession(): Session { - return this.screenSession; - } - - /** - * @internal - */ - isScreenSessionConnected(): boolean { - return !!this.screenSession.capabilities; - } - - /** - * @internal - * Whether the STT service is ready or not - * This will be `false` when the app receives a SPEECH_TO_TEXT_DISCONNECTED exception - * and it cannot subscribe to STT - */ - isSttReady(): boolean { - return this._isSttReady.getValue(); - } - - /** - * @internal - */ - setSTTReady(value: boolean): void { - if (this._isSttReady.getValue() !== value) { - this._isSttReady.next(value); - } - } - - /** - * @internal - * Connects to webcam session using webcam token. - */ - async connectWebcamSession(participantId: string, nickname: string): Promise { - if (this.isWebcamSessionConnected()) { - this.log.d('Webcam session is already connected'); - return undefined; - } - - this.log.d('Connecting webcam session'); - await this.webcamSession.connect(this.getWebcamToken(), { - clientData: nickname, - participantId, - type: VideoType.CAMERA - }); - - return this.webcamSession.connection.connectionId; - } - - /** - * @internal - * Connects to screen session using screen token. - */ - async connectScreenSession(participantId: string, nickname: string): Promise { - if (this.isScreenSessionConnected()) { - this.log.d('Screen session is already connected'); - return undefined; - } - - this.log.d('Connecting screen session'); - await this.screenSession.connect(this.getScreenToken(), { - clientData: `${nickname}_${VideoType.SCREEN}`, - participantId, - type: VideoType.SCREEN - }); - - return this.screenSession.connection.connectionId; - } - - /** - * Leaves the session, destroying all local streams and clean all participant data. - */ - disconnect() { - this.disconnectSession(this.webcamSession); - this.disconnectSession(this.screenSession); - } - - /** - * - * Apply the new resolution to the video stream if it is CAMERA type. - * @param streamManager - * @internal - */ - async updateVideoEncodingParameters(streamManager: StreamManager) { - if ( - !streamManager || - !streamManager.stream || - !streamManager.stream.getMediaStream() || - streamManager.stream.typeOfVideo === VideoType.SCREEN - ) { - return; - } - - const track = streamManager?.stream.getMediaStream().getVideoTracks()[0]; - const videoSender = streamManager?.stream - .getRTCPeerConnection() - .getSenders() - .find((sender) => sender.track === track); - - if (!videoSender) return; - - const parameters: RTCRtpSendParameters = videoSender.getParameters(); - const desiredFrameRate = this.libService.getStreamFrameRate(); - const desiredWidth = Number(this.libService.getStreamResolution().split('x')[0]); - const desiredResolution = Number(track?.getConstraints()?.width) / desiredWidth ?? 1.0; - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters) => { - if (desiredFrameRate > 0 && encoding['maxFramerate'] !== desiredFrameRate) encoding['maxFramerate'] = desiredFrameRate; - if (desiredResolution >= 1 && encoding['scaleResolutionDownBy'] !== desiredResolution) { - encoding['scaleResolutionDownBy'] = desiredResolution; - } - }); - await videoSender.setParameters(parameters); - } - - /** - * @internal - * Initialize a publisher checking devices saved on storage or if participant have devices available. - */ - async initDefaultPublisher(pp?: Partial): Promise { - const hasVideoDevices = this.deviceService.hasVideoDeviceAvailable(); - const hasAudioDevices = this.deviceService.hasAudioDeviceAvailable(); - const isVideoActive = !this.deviceService.isVideoMuted(); - const isAudioActive = !this.deviceService.isAudioMuted(); - - let videoSource: string | boolean = false; - let audioSource: string | boolean = false; - - if (hasVideoDevices) { - // Video is active, assign the device selected - videoSource = this.deviceService.getCameraSelected()?.device ?? false; - } else if (!isVideoActive && hasVideoDevices) { - // Video is muted, assign the default device - // videoSource = undefined; - } - - if (hasAudioDevices) { - // Audio is active, assign the device selected - audioSource = this.deviceService.getMicrophoneSelected()?.device ?? false; - } else if (!isAudioActive && hasAudioDevices) { - // Audio is muted, assign the default device - // audioSource = undefined; - } - - const mirror = this.deviceService.getCameraSelected() && this.deviceService.getCameraSelected()?.type === CameraType.FRONT; - const properties: PublisherProperties = { - videoSource, - audioSource, - publishVideo: isVideoActive, - publishAudio: isAudioActive, - resolution: pp?.resolution ?? '640x480', - frameRate: pp?.frameRate ?? 30, - videoSimulcast: pp?.videoSimulcast ?? false, - mirror - }; - if (hasVideoDevices || hasAudioDevices) { - return this.initPublisher(properties); - } - } - - /** - * @internal - */ - private initPublisher(properties: PublisherProperties, targetElement?: string | HTMLElement): Promise { - this.log.d('Initializing publisher with properties: ', properties); - return this.OV.initPublisherAsync(targetElement, properties); - } - - /** - * @internal - * @param hasAudio - * @returns - */ - initScreenPublisher(): Promise { - const properties: PublisherProperties = { - videoSource: ScreenType.SCREEN, - audioSource: ScreenType.SCREEN, - publishVideo: true, - publishAudio: true, - mirror: false - }; - return this.initPublisher(properties); - } - - /** - * Publishes the publisher to the webcam Session - * @param publisher - */ - async publishCamera(publisher: Publisher): Promise { - if (!publisher) return; - if (this.webcamSession?.capabilities?.publish) { - return this.webcamSession.publish(publisher); - } - this.log.e('Webcam publisher cannot be published'); - } - - /** - * Publishes the publisher to the screen Session - * @param publisher - */ - async publishScreen(publisher: Publisher): Promise { - if (!publisher) return; - - if (this.screenSession?.capabilities?.publish) { - return this.screenSession.publish(publisher); - } - this.log.e('Screen publisher cannot be published'); - } - - /** - * Unpublishes the publisher of the webcam Session - * @param publisher - */ - async unpublishCamera(publisher: Publisher): Promise { - if (!publisher) return; - return this.webcamSession.unpublish(publisher); - } - - /** - * Unpublishes the publisher of the screen Session - * @param publisher - */ - async unpublishScreen(publisher: Publisher): Promise { - if (!publisher) return; - return this.screenSession.unpublish(publisher); - } - - /** - * Publish or unpublish the video stream (if available). - * It hides the camera muted stream if screen is sharing. - * See openvidu-browser {@link https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Publisher.html#publishVideo publishVideo} - * - * @deprecated This method has been moved to ParticipantService - * - * TODO: Remove this method in release 2.29.0 - */ - async publishVideo(publish: boolean): Promise { - const participantService = this.injector.get(ParticipantService); - return participantService.publishVideo(publish); - } - - /** - * Share or unshare the screen. - * Hide the camera muted stream when screen is sharing. - * @deprecated This method has been moved to ParticipantService - * - * TODO: Remove this method in release 2.29.0 - */ - async toggleScreenshare() { - const participantService = this.injector.get(ParticipantService); - return participantService.toggleScreenshare(); - } - - /** - * - * Publish or unpublish the audio stream (if available). - * See openvidu-browser {@link https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Publisher.html#publishAudio publishAudio}. - * @deprecated This method has been moved to ParticipantService - * - * TODO: Remove this method in release 2.29.0 - */ - publishAudio(publish: boolean): void { - const participantService = this.injector.get(ParticipantService); - participantService.publishAudio(publish); - } - - /** - * @internal - * - * @param type: type of signal - * @param connections: if undefined, the signal will be sent to all participants - */ - sendSignal(type: Signal, connections?: Connection[], data?: any): Promise { - const signalOptions: SignalOptions = { - data: JSON.stringify(data), - type, - to: connections && connections.length > 0 ? connections : undefined - }; - return this.webcamSession.signal(signalOptions); - } - - /** - * @internal - * @param cameraPublisher - * @param props - */ - async replaceCameraTrack(cameraPublisher: Publisher, props: PublisherProperties) { - const isReplacingAudio = !!props.audioSource; - const isReplacingVideo = !!props.videoSource; - let mediaStream: MediaStream | undefined; - let track: MediaStreamTrack | undefined; - - try { - if (isReplacingVideo || isReplacingAudio) { - mediaStream = await this.createMediaStream(props); - } - - if (isReplacingVideo) { - track = mediaStream?.getVideoTracks()[0]; - } else if (isReplacingAudio) { - track = mediaStream?.getAudioTracks()[0]; - } - - if (track) { - await cameraPublisher.replaceTrack(track); - } - } catch (error) { - this.log.e('Error replacing track ', error); - } - } - - /** - * @internal - * @param screenPublisher - * @param props - */ - async replaceScreenTrack(screenPublisher: Publisher, props: PublisherProperties) { - try { - let newScreenMediaStream = await this.OVScreen.getUserMedia(props); - screenPublisher.stream.getMediaStream().getVideoTracks()[0].stop(); - await screenPublisher.replaceTrack(newScreenMediaStream.getVideoTracks()[0]); - } catch (error) { - this.log.w('Cannot create the new MediaStream', error); - } - } - - /** - * @internal - * Subscribe all `CAMERA` stream types to speech-to-text - * It will retry the subscription each `STT_TIMEOUT_MS` - * - * @param lang The language of the Stream's audio track. - */ - async subscribeRemotesToSTT(lang: string): Promise { - const participantService = this.injector.get(ParticipantService); - - const remoteParticipants = participantService.getRemoteParticipants(); - let successNumber = 0; - - for (const p of remoteParticipants) { - const stream = p.getCameraConnection()?.streamManager?.stream; - if (stream) { - try { - await this.subscribeStreamToStt(stream, lang); - successNumber++; - } catch (error) { - this.log.e(`Error subscribing ${stream.streamId} to STT:`, error); - break; - } - } - } - - this.setSTTReady(successNumber === remoteParticipants.length); - if (!this.isSttReady()) { - this.log.w('STT is not ready. Retrying subscription...'); - this.sttReconnectionTimeout = setTimeout(this.subscribeRemotesToSTT.bind(this, lang), this.STT_TIMEOUT_MS); - } - } - - /** - * @internal - * Subscribe a stream to speech-to-text - * @param stream - * @param lang - */ - async subscribeStreamToStt(stream: Stream, lang: string): Promise { - await this.getWebcamSession().subscribeToSpeechToText(stream, lang); - this.log.d(`Subscribed stream ${stream.streamId} to STT with ${lang} language.`); - } - - /** - * @internal - * Unsubscribe to all `CAMERA` stream types to speech-to-text if STT is up(ready) - */ - async unsubscribeRemotesFromSTT(): Promise { - const participantService = this.injector.get(ParticipantService); - - clearTimeout(this.sttReconnectionTimeout); - if (this.isSttReady()) { - for (const p of participantService.getRemoteParticipants()) { - const stream = p.getCameraConnection().streamManager.stream; - if (stream) { - try { - await this.getWebcamSession().unsubscribeFromSpeechToText(stream); - } catch (error) { - this.log.e(`Error unsubscribing ${stream.streamId} from STT:`, error); - } - } - } - } - } - - /** - * @internal - * @param pp {@link PublisherProperties} - * @returns Promise - */ - async createMediaStream(pp: PublisherProperties): Promise { - const participantService = this.injector.get(ParticipantService); - const currentCameraSelected = this.deviceService.getCameraSelected(); - const currentMicSelected = this.deviceService.getMicrophoneSelected(); - const isReplacingAudio = Boolean(pp.audioSource); - const isReplacingVideo = Boolean(pp.videoSource); - - try { - const trackType = isReplacingAudio ? 'audio' : 'video'; - this.forceStopMediaTracks(participantService.getMyCameraPublisher().stream.getMediaStream(), trackType); - return this.OV.getUserMedia(pp); - } catch (error) { - this.log.w('Error creating MediaStream', error); - if ((error).name === OpenViduErrorName.DEVICE_ACCESS_DENIED) { - this.log.w('The device requested is not available. Restoring the older one'); - // The track requested is not available so we are getting the old tracks ids for recovering the track - if (isReplacingVideo) { - pp.videoSource = currentCameraSelected?.device; - } else if (isReplacingAudio) { - pp.audioSource = currentMicSelected?.device; - } - // TODO show error alert informing that the new device is not available - return this.OV.getUserMedia(pp); - } - throw error; - } - } - - /** - * @internal - */ - myNicknameHasBeenChanged(): boolean { - const participantService = this.injector.get(ParticipantService); - let oldNickname: string = ''; - try { - const connData = JSON.parse(this.cleanConnectionData(this.webcamSession.connection.data)); - oldNickname = connData.clientData; - } catch (error) { - this.log.e(error); - } finally { - return oldNickname !== participantService.getMyNickname(); - } - } - - /** - * @internal - */ - isMyOwnConnection(connectionId: string): boolean { - return ( - this.webcamSession?.connection?.connectionId === connectionId || this.screenSession?.connection?.connectionId === connectionId - ); - } - - /** - * - * Returns the remote connections of the Session. - * See {@link https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Connection.html Connection} object. - */ - getRemoteConnections(): Connection[] { - // Avoid screen connections - const remoteCameraConnections: Connection[] = Array.from(this.webcamSession.remoteConnections.values()).filter((conn) => { - let type: VideoType; - type = JSON.parse(this.cleanConnectionData(conn.data)).type; - return type !== VideoType.SCREEN; - }); - return remoteCameraConnections; - } - - private disconnectSession(session: Session) { - if (session) { - if (session.sessionId === this.webcamSession?.sessionId) { - this.log.d('Disconnecting webcam session'); - this.webcamSession?.disconnect(); - this.webcamSession = null; - } else if (session.sessionId === this.screenSession?.sessionId) { - this.log.d('Disconnecting screen session'); - this.screenSession?.disconnect(); - this.screenSession = null; - } - } - } - - private cleanConnectionData(data: string): string { - return data.split('%/%')[0]; - } - - private forceStopMediaTracks(stream: MediaStream, type: 'video' | 'audio'): void { - if (stream) { - stream.getTracks().forEach((track) => { - if (track.kind === type) { - track.stop(); - track.enabled = false; - } - }); - } - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/participant/participant.service.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/participant/participant.service.ts deleted file mode 100644 index a3eade0b..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/participant/participant.service.ts +++ /dev/null @@ -1,531 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Publisher, PublisherProperties, Subscriber } from 'openvidu-browser-v2compatibility'; -import { BehaviorSubject, Observable } from 'rxjs'; -import { ILogger } from '../../models/logger.model'; -import { - OpenViduRole, - ParticipantAbstractModel, - ParticipantModel, - ParticipantProperties, - StreamModel -} from '../../models/participant.model'; -import { VideoType } from '../../models/video-type.model'; -import { OpenViduAngularConfigService } from '../config/openvidu-angular.config.service'; -import { DeviceService } from '../device/device.service'; -import { LoggerService } from '../logger/logger.service'; -import { OpenViduService } from '../openvidu/openvidu.service'; -import { CustomDevice } from '../../models/device.model'; - -@Injectable({ - providedIn: 'root' -}) -export class ParticipantService { - /** - * Local participant Observable which pushes the local participant object in every update. - */ - localParticipantObs: Observable; - protected _localParticipant: BehaviorSubject = new BehaviorSubject( - null - ); - - /** - * Remote participants Observable which pushes the remote participants array in every update. - */ - remoteParticipantsObs: Observable; - protected _remoteParticipants: BehaviorSubject = new BehaviorSubject([]); - - protected localParticipant: ParticipantAbstractModel; - protected remoteParticipants: ParticipantAbstractModel[] = []; - - protected log: ILogger; - - /** - * @internal - */ - constructor( - protected openviduAngularConfigSrv: OpenViduAngularConfigService, - private openviduService: OpenViduService, - private deviceService: DeviceService, - protected loggerSrv: LoggerService - ) { - this.log = this.loggerSrv.get('ParticipantService'); - this.localParticipantObs = this._localParticipant.asObservable(); - this.remoteParticipantsObs = this._remoteParticipants.asObservable(); - } - - /** - * @internal - */ - initLocalParticipant(props: ParticipantProperties) { - this.localParticipant = this.newParticipant(props); - this.updateLocalParticipant(); - } - - getLocalParticipant(): ParticipantAbstractModel { - return this.localParticipant; - } - - /** - * Publish or unpublish the local participant video stream (if available). - * It hides the camera stream (while muted) if screen is sharing. - * See openvidu-browser {@link https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Publisher.html#publishVideo publishVideo} - * - */ - async publishVideo(publish: boolean): Promise { - const cameraPublisher = this.getMyCameraPublisher(); - await this.publishVideoAux(cameraPublisher, publish); - this.updateLocalParticipant(); - } - - /** - * Publish or unpublish the local participant audio stream (if available). - * See openvidu-browser {@link https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Publisher.html#publishAudio publishAudio}. - * - */ - publishAudio(publish: boolean): void { - if (this.isMyCameraActive()) { - this.publishAudioAux(this.getMyCameraPublisher(), publish); - this.updateLocalParticipant(); - } - } - - async replaceVideoTrack(device: CustomDevice) { - const mirror = this.deviceService.cameraNeedsMirror(device.device); - const cameraPublisher = this.getMyCameraPublisher(); - const { frameRate, videoDimensions } = cameraPublisher.stream; - const pp: PublisherProperties = { - videoSource: device.device, - audioSource: false, - frameRate, - resolution: `${videoDimensions.width}x${videoDimensions.height}`, - mirror - }; - await this.openviduService.replaceCameraTrack(cameraPublisher, pp); - } - - /** - * Share or unshare the local participant screen. - * Hide the camera stream (while muted) when screen is sharing. - * - */ - async toggleScreenshare() { - const screenPublisher = this.getMyScreenPublisher(); - const participantNickname = this.getMyNickname(); - const participantId = this.getLocalParticipant().id; - - if (this.localParticipant.hasCameraAndScreenActives()) { - // Disabling screenShare - this.disableScreenStream(); - this.updateLocalParticipant(); - this.openviduService.unpublishScreen(screenPublisher); - } else if (this.localParticipant.hasOnlyCameraActive()) { - // I only have the camera published - const screenPublisher = await this.openviduService.initScreenPublisher(); - - screenPublisher.once('accessAllowed', async () => { - // Listen to event fired when native stop button is clicked - screenPublisher.stream - .getMediaStream() - .getVideoTracks()[0] - .addEventListener('ended', async () => { - this.log.d('Clicked native stop button. Stopping screen sharing'); - await this.toggleScreenshare(); - }); - - // Enabling screenShare - this.activeMyScreenShare(screenPublisher); - - if (!this.openviduService.isScreenSessionConnected()) { - await this.openviduService.connectScreenSession(participantId, participantNickname); - } - await this.openviduService.publishScreen(screenPublisher); - }); - - screenPublisher.once('accessDenied', (error: any) => { - return Promise.reject(error); - }); - } - } - - /** - * @internal - */ - getMyCameraPublisher(): Publisher { - return this.localParticipant.getCameraConnection().streamManager; - } - - /** - * @internal - */ - setMyCameraPublisher(publisher: Publisher | undefined) { - this.localParticipant.setCameraPublisher(publisher); - } - /** - * @internal - */ - setMyCameraConnectionId(connectionId: string) { - this.localParticipant.setCameraConnectionId(connectionId); - } - - /** - * @internal - */ - getMyScreenPublisher(): Publisher { - return this.localParticipant.getScreenConnection()?.streamManager; - } - - /** - * @internal - */ - setMyScreenPublisher(publisher: Publisher) { - this.localParticipant.setScreenPublisher(publisher); - } - - /** - * @internal - */ - setMyScreenConnectionId(connectionId: string) { - this.localParticipant.setScreenConnectionId(connectionId); - } - - /** - * @internal - */ - enableWebcamStream() { - this.localParticipant.enableCamera(); - } - - /** - * @internal - */ - disableWebcamStream() { - this.localParticipant.disableCamera(); - } - - /** - * @internal - */ - activeMyScreenShare(screenPublisher: Publisher) { - this.log.d('Enabling screen publisher'); - - const steramModel: StreamModel = { - type: VideoType.SCREEN, - videoEnlarged: true, - streamManager: screenPublisher, - connected: true, - connectionId: '' - }; - - this.resetRemoteStreamsToNormalSize(); - this.resetMyStreamsToNormalSize(); - this.localParticipant.addConnection(steramModel); - this.updateLocalParticipant(); - } - - /** - * @internal - */ - disableScreenStream() { - this.localParticipant.disableScreen(); - } - - /** - * @internal - */ - setMyNickname(nickname: string) { - this.localParticipant.setNickname(nickname); - this.updateLocalParticipant(); - } - - /** - * @internal - */ - getMyNickname(): string { - return this.localParticipant.nickname; - } - - getMyRole(): string { - return this.localParticipant.getRole(); - } - - amIModerator(): boolean { - return this.getMyRole() === OpenViduRole.MODERATOR; - } - - /** - * @internal - */ - toggleMyVideoEnlarged(connectionId: string) { - this.localParticipant.toggleVideoEnlarged(connectionId); - } - - /** - * @internal - */ - resetMyStreamsToNormalSize() { - if (this.localParticipant.someHasVideoEnlarged()) { - this.localParticipant.setAllVideoEnlarged(false); - this.updateLocalParticipant(); - } - } - - /** - * @internal - */ - async clear() { - await this.getMyCameraPublisher()?.stream?.disposeMediaStream(); - await this.getMyScreenPublisher()?.stream?.disposeMediaStream(); - this.disableScreenStream(); - this.remoteParticipants = []; - this.updateRemoteParticipants(); - this.updateLocalParticipant(); - } - - /** - * @internal - */ - isMyCameraActive(): boolean { - return this.localParticipant.isCameraActive(); - } - - isMyVideoActive(): boolean { - return this.localParticipant.isCameraVideoActive(); - } - - isMyAudioActive(): boolean { - return this.localParticipant?.hasAudioActive(); - } - - /** - * @internal - */ - hasScreenAudioActive(): boolean { - return this.localParticipant.isScreenAudioActive(); - } - - /** - * Force to update the local participant object and fire a new {@link localParticipantObs} Observable event. - */ - updateLocalParticipant() { - this._localParticipant.next( - Object.assign(Object.create(Object.getPrototypeOf(this.localParticipant)), { ...this.localParticipant }) - ); - } - - private publishAudioAux(publisher: Publisher, value: boolean): void { - if (!!publisher) { - publisher.publishAudio(value); - } - } - - /** - * @internal - */ - private async publishVideoAux(publisher: Publisher, publish: boolean): Promise { - if (!!publisher) { - let resource: boolean | MediaStreamTrack = true; - if (publish) { - // Forcing restoration with a custom media stream (the older one instead the default) - const currentDeviceId = this.deviceService.getCameraSelected()?.device; - const { frameRate, videoDimensions } = publisher.stream; - const mediaStream = await this.openviduService.createMediaStream({ - videoSource: currentDeviceId, - audioSource: false, - frameRate, - resolution: `${videoDimensions.width}x${videoDimensions.height}`, - }); - resource = mediaStream.getVideoTracks()[0]; - } - - await publisher.publishVideo(publish, resource); - } - } - - /** - * REMOTE USERS - */ - - /** - * @internal - */ - addRemoteConnection(connectionId: string, data: string, subscriber: Subscriber) { - const type: VideoType = this.getTypeConnectionData(data); - const streamModel: StreamModel = { - type, - videoEnlarged: type === VideoType.SCREEN, - streamManager: subscriber, - connected: true, - connectionId - }; - - // Avoiding create a new participant if participantId param is not exist in connection data - // participant Id is necessary for allowing to have multiple connection in one participant - const participantId = this.getParticipantIdFromData(data) || connectionId; - - const participantAdded = this.getRemoteParticipantById(participantId); - if (!!participantAdded) { - this.log.d('Adding connection to existing participant: ', participantId); - if (participantAdded.hasConnectionType(streamModel.type)) { - this.log.d('Participant has publisher, updating it'); - participantAdded.setPublisher(streamModel.type, subscriber); - } else { - this.log.d('Participant has not publisher, adding it'); - if (streamModel.type === VideoType.SCREEN) { - this.resetRemoteStreamsToNormalSize(); - this.resetMyStreamsToNormalSize(); - } - participantAdded.addConnection(streamModel); - } - } else { - this.log.w('Creating new participant with id: ', participantId); - const props: ParticipantProperties = { - nickname: this.getNicknameFromConnectionData(data), - local: false, - id: participantId - }; - const remoteParticipant = this.newParticipant(props, streamModel); - this.remoteParticipants.push(remoteParticipant); - } - this.updateRemoteParticipants(); - } - - getRemoteParticipants(): ParticipantAbstractModel[] { - return this.remoteParticipants; - } - - /** - * @internal - */ - resetRemoteStreamsToNormalSize() { - this.remoteParticipants.forEach((participant) => participant.setAllVideoEnlarged(false)); - this.updateRemoteParticipants(); - } - - /** - * @internal - */ - removeConnectionByConnectionId(connectionId: string) { - this.log.w('Deleting connection: ', connectionId); - let participant: ParticipantAbstractModel | undefined; - if (this.localParticipant.hasConnectionId(connectionId)) { - participant = this.localParticipant; - } else { - participant = this.getRemoteParticipantByConnectionId(connectionId); - } - - if (participant) { - const removeStream: StreamModel = participant.removeConnection(connectionId); - //TODO: Timeout of X seconds?? Its possible sometimes the connections map was empty but must not be deleted - if (participant.streams.size === 0) { - // Remove participants without connections - this.remoteParticipants = this.remoteParticipants.filter((p) => p !== participant); - } - if (removeStream.type === VideoType.SCREEN) { - const remoteScreens = this.remoteParticipants.filter((p) => p.isScreenActive()); - if (remoteScreens.length > 0) { - // Enlarging the last screen connection active - const lastScreenActive = remoteScreens[remoteScreens.length - 1]; - lastScreenActive.setScreenEnlarged(true); - } else if (this.localParticipant.isScreenActive()) { - // Enlarging my screen if thereare not any remote screen active - this.localParticipant.setScreenEnlarged(true); - } - } - - this.updateRemoteParticipants(); - } - } - /** - * @internal - */ - getRemoteParticipantByConnectionId(connectionId: string): ParticipantAbstractModel | undefined { - return this.remoteParticipants.find((p) => p.hasConnectionId(connectionId)); - } - - protected getRemoteParticipantById(id: string): ParticipantAbstractModel | undefined { - return this.remoteParticipants.find((p) => p.id === id); - } - /** - * @internal - */ - someoneIsSharingScreen(): boolean { - return this.remoteParticipants.some((p) => p.someHasVideoEnlarged()); - } - - /** - * @internal - */ - toggleRemoteVideoEnlarged(connectionId: string) { - const participant = this.getRemoteParticipantByConnectionId(connectionId); - participant?.toggleVideoEnlarged(connectionId); - } - - /** - * @internal - */ - getNicknameFromConnectionData(data: string): string { - try { - const dataClean = data.replace('%/%{}', ''); - return JSON.parse(dataClean).clientData; - } catch (error) { - return 'OpenVidu_User'; - } - } - - /** - * @internal - */ - setRemoteNickname(connectionId: string, nickname: string) { - const participant = this.getRemoteParticipantByConnectionId(connectionId); - if (participant) { - participant.setNickname(nickname); - this.updateRemoteParticipants(); - } - } - - /** - * @internal - */ - setRemoteMutedForcibly(id: string, value: boolean) { - const participant = this.getRemoteParticipantById(id); - if (participant) { - participant.setMutedForcibly(value); - this.updateRemoteParticipants(); - } - } - - /** - * Force to update the remote participants object and fire a new {@link remoteParticipantsObs} Observable event. - */ - updateRemoteParticipants() { - this._remoteParticipants.next([...this.remoteParticipants]); - } - - /** - * @internal - * @param data - * @returns Stream video type - */ - getTypeConnectionData(data: string): VideoType { - try { - return JSON.parse(data).type; - } catch (error) { - return VideoType.CAMERA; - } - } - - protected getParticipantIdFromData(data: string): string { - try { - return JSON.parse(data).participantId; - } catch (error) { - return ''; - } - } - - protected newParticipant(props: ParticipantProperties, streamModel?: StreamModel) { - if (this.openviduAngularConfigSrv.hasParticipantFactory()) { - return this.openviduAngularConfigSrv.getParticipantFactory().apply(this, [props, streamModel]); - } - return new ParticipantModel(props, streamModel); - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/recording/recording.service.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/recording/recording.service.ts deleted file mode 100644 index 66fb479f..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/recording/recording.service.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { Injectable } from '@angular/core'; -import { RecordingEvent } from 'openvidu-browser-v2compatibility'; -import { BehaviorSubject, Observable, Subject } from 'rxjs'; -import { RecordingInfo, RecordingStatus } from '../../models/recording.model'; -import { ActionService } from '../action/action.service'; - -@Injectable({ - providedIn: 'root' -}) -export class RecordingService { - /** - * Recording status Observable which pushes the recording state in every update. - */ - recordingStatusObs: Observable<{ info: RecordingInfo; time?: Date } | undefined>; - - /** - * @internal - */ - forceUpdateRecordingsObs: Subject = new Subject(); - private recordingTime: Date | undefined; - private recordingTimeInterval: NodeJS.Timeout; - private currentRecording: RecordingInfo = { status: RecordingStatus.STOPPED }; - private recordingStatus = >new BehaviorSubject(undefined); - private baseUrl = '/' + (!!window.location.pathname.split('/')[1] ? window.location.pathname.split('/')[1] + '/' : ''); - - /** - * @internal - */ - constructor(private actionService: ActionService) { - this.recordingStatusObs = this.recordingStatus.asObservable(); - } - - /** - * @param status {@link RecordingStatus} - * Update the recording status. This method is used by the OpenVidu Angular library to update the recording status. - */ - updateStatus(status: RecordingStatus) { - this.currentRecording = { - status: status - }; - this.recordingStatus.next({ info: this.currentRecording }); - } - - /** - * @internal - * @param event - */ - startRecording(event: RecordingEvent) { - this.currentRecording = { - status: RecordingStatus.STARTED, - id: event.id, - name: event.name, - reason: event.reason - }; - this.startRecordingTime(); - this.recordingStatus.next({ info: this.currentRecording, time: this.recordingTime }); - } - - /** - * @internal - * @param event - */ - stopRecording(event: RecordingEvent) { - this.currentRecording.status = RecordingStatus.STOPPED; - this.currentRecording.reason = event.reason; - this.recordingStatus.next({ info: this.currentRecording, time: undefined }); - this.stopRecordingTime(); - } - - /** - * @internal - * Play the recording blob received as parameter. This parameter must be obtained from backend using the OpenVidu REST API - */ - playRecording(recording: RecordingInfo) { - const recordingId = recording.id; - // Only COMPOSED recording is supported. The extension will allways be 'mp4'. - const extension = 'mp4'; //recording.url?.split('.').pop() || 'mp4'; - const queryParamForAvoidCache = `?t=${new Date().getTime()}`; - this.actionService.openRecordingPlayerDialog( - `${this.baseUrl}recordings/${recordingId}/${recordingId}.${extension}${queryParamForAvoidCache}` - ); - } - - /** - * @internal - * Download the the recording file received . - * @param recording - */ - downloadRecording(recording: RecordingInfo) { - const recordingId = recording.id; - // Only COMPOSED recording is supported. The extension will allways be 'mp4'. - const extension = 'mp4'; //recording.url?.split('.').pop() || 'mp4'; - const link = document.createElement('a'); - link.href = `/recordings/${recordingId}/${recordingId}.${extension}`; - link.download = `${recordingId}.${extension}`; - link.dispatchEvent( - new MouseEvent('click', { - bubbles: true, - cancelable: true, - view: window - }) - ); - - setTimeout(() => { - // For Firefox it is necessary to delay revoking the ObjectURL - link.remove(); - }, 100); - } - - forceUpdateRecordings() { - this.forceUpdateRecordingsObs.next(); - } - - private startRecordingTime() { - this.recordingTime = new Date(); - this.recordingTime.setHours(0, 0, 0, 0); - this.recordingTimeInterval = setInterval(() => { - if (this.recordingTime) { - this.recordingTime.setSeconds(this.recordingTime.getSeconds() + 1); - this.recordingTime = new Date(this.recordingTime.getTime()); - this.recordingStatus.next({ info: this.currentRecording, time: this.recordingTime }); - } - }, 1000); - } - - private stopRecordingTime() { - clearInterval(this.recordingTimeInterval); - this.recordingTime = undefined; - } -} diff --git a/openvidu-components-angular/projects/openvidu-angular/tsconfig.lib.json b/openvidu-components-angular/projects/openvidu-angular/tsconfig.lib.json deleted file mode 100644 index d4f03ff6..00000000 --- a/openvidu-components-angular/projects/openvidu-angular/tsconfig.lib.json +++ /dev/null @@ -1,24 +0,0 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "../../dist/openvidu-angular", - "module": "esnext", - "moduleResolution": "node", - "lib": ["ES2020", "dom"], - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "declaration": true, - "sourceMap": true, - "declarationMap": true, - "inlineSources": true, - "resolveJsonModule": true, - "types": [] - }, - "exclude": [ - "node_modules", - "src/test.ts", - "**/*.spec.ts", - "**/*.mock.ts" - ] -} diff --git a/openvidu-components-angular/projects/openvidu-angular/.browserslistrc b/openvidu-components-angular/projects/openvidu-components-angular/.browserslistrc similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/.browserslistrc rename to openvidu-components-angular/projects/openvidu-components-angular/.browserslistrc diff --git a/openvidu-components-angular/projects/openvidu-angular/.prettierrc b/openvidu-components-angular/projects/openvidu-components-angular/.prettierrc similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/.prettierrc rename to openvidu-components-angular/projects/openvidu-components-angular/.prettierrc diff --git a/openvidu-components-angular/projects/openvidu-components-angular/README.md b/openvidu-components-angular/projects/openvidu-components-angular/README.md new file mode 100644 index 00000000..4d38254a --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/README.md @@ -0,0 +1,95 @@ +# Welcome to OpenVidu Components Angular + +Build powerful video conferencing applications with ease using OpenVidu Components Angular. + +## Introduction + +Angular Components are the simplest way to create real-time videoconferencing apps with Angular. There's no need to manage state or low-level events; Angular Components from OpenVidu handle all the complexity for you. + +## Getting Started + +To get started with OpenVidu Components Angular, visit our [**Getting Started guide**](https://openvidu.io/docs/ui-components/angular-components/). + +1. Create an Angular Project (>= 17.0.0) + + ```bash + ng new your-project-name + ``` + +2. Add Angular Material to your project + + ```bash + ng add @angular/material + ``` + +3. Install OpenVidu Components Angular + + ```bash + npm install openvidu-components-angular + ``` + +4. Import and use OpenVidu Components Angular + + ```typescript + import { OpenViduComponentsModule, OpenViduComponentsConfig } from 'openvidu-components-angular'; + + // Other imports ... + + const config: OpenViduComponentsConfig = { + production: environment.production + }; + + bootstrapApplication(AppComponent, { + providers: [ + importProvidersFrom( + OpenViduComponentsModule.forRoot(config) + // Other imports ... + ), + provideAnimations() + ] + }).catch((err) => console.error(err)); + ``` + +You can also customize the styles in your `styles.scss` file: + +```scss +:root { + --ov-primary-color: #303030; + --ov-secondary-color: #3e3f3f; + --ov-tertiary-color: #598eff; + --ov-warn-color: #eb5144; + --ov-accent-color: #ffae35; + --ov-light-color: #e6e6e6; + + --ov-logo-background-color: #3a3d3d; + + --ov-text-color: #ffffff; + + --ov-panel-text-color: #1d1d1d; + --ov-panel-background: #ffffff; + + --ov-buttons-radius: 50%; + --ov-leave-button-radius: 10px; + --ov-video-radius: 5px; + --ov-panel-radius: 5px; +} +``` + +## Usage + +```html + + +``` + +## API Documentation + +For detailed information on OpenVidu Angular Components, refer to our [**API Reference**](https://openvidu.io/docs/reference-docs/openvidu-components-angular). + +## Support + +If you have any questions or need assistance, please reach out to our [**Support page**](https://openvidu.io/support/). diff --git a/openvidu-components-angular/projects/openvidu-components-angular/doc/.compodocrc.json b/openvidu-components-angular/projects/openvidu-components-angular/doc/.compodocrc.json new file mode 100644 index 00000000..f710dcba --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/doc/.compodocrc.json @@ -0,0 +1,30 @@ +{ + "name": "OpenVidu Components Angular Documentation", + "output": "./docs/openvidu-components-angular", + "hideGenerator": true, + "disableLifeCycleHooks": true, + "disableProtected": true, + "disableInternal": true, + "disablePrivate": true, + "disableCoverage": true, + "disableRoutesGraph": true, + "disableSourceCode": true, + "disableTemplateTab": true, + "disableDomTree": true, + "disableStyleTab": true, + "disableDependencies": true, + "theme": "gitbook", + "customFavicon": "./projects/openvidu-components-angular/doc/favicon.ico", + "extTheme": "./projects/openvidu-components-angular/doc/styles", + "navTabConfig": [ + { + "id": "info", + "label": "Info" + }, + { + "id": "readme", + "label": "Directives" + } + ], + "tsconfig": "./projects/openvidu-components-angular/doc/tsconfig.doc.json" +} diff --git a/openvidu-components-angular/projects/openvidu-angular/doc/favicon.ico b/openvidu-components-angular/projects/openvidu-components-angular/doc/favicon.ico similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/doc/favicon.ico rename to openvidu-components-angular/projects/openvidu-components-angular/doc/favicon.ico diff --git a/openvidu-components-angular/projects/openvidu-components-angular/doc/scripts/generate-directive-tables.js b/openvidu-components-angular/projects/openvidu-components-angular/doc/scripts/generate-directive-tables.js new file mode 100644 index 00000000..db47bc84 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/doc/scripts/generate-directive-tables.js @@ -0,0 +1,170 @@ +const fs = require('fs'); +const glob = require('glob'); + +const startApiLine = ''; +const apiDirectivesTable = + '| **Parameter** | **Type** | **Reference** | \n' + + '|:--------------------------------: | :-------: | :---------------------------------------------: |'; +const endApiLine = ''; + +function getDirectiveFiles() { + // Directory where directive files are located + const directivesDir = 'projects/openvidu-components-angular/src/lib/directives/api'; + return listFiles(directivesDir, '.directive.ts'); +} + +function getComponentFiles() { + // Directory where component files are located + const componentsDir = 'projects/openvidu-components-angular/src/lib/components'; + return listFiles(componentsDir, '.component.ts'); +} + +function getAdminFiles() { + // Directory where component files are located + const componentsDir = 'projects/openvidu-components-angular/src/lib/admin'; + return listFiles(componentsDir, '.component.ts'); +} + +function listFiles(directoryPath, fileExtension) { + const files = glob.sync(`${directoryPath}/**/*${fileExtension}`); + if (files.length === 0) { + throw new Error(`No ${fileExtension} files found in ${directoryPath}`); + } + return files; +} + +function initializeDynamicTableContent(filePath) { + replaceDynamicTableContent(filePath, apiDirectivesTable); +} + +function removeApiTableContent(filePath) { + const content = '_No API directives available for this component_. \n'; + replaceDynamicTableContent(filePath, content); +} + +function apiTableContentIsEmpty(filePath) { + try { + const data = fs.readFileSync(filePath, 'utf8'); + const startIdx = data.indexOf(startApiLine); + const endIdx = data.indexOf(endApiLine); + if (startIdx !== -1 && endIdx !== -1) { + const capturedContent = data.substring(startIdx + startApiLine.length, endIdx).trim(); + return capturedContent === apiDirectivesTable; + } + return false; + } catch (error) { + return false; + } +} + +function writeApiDirectivesTable(componentFiles, directiveFiles) { + componentFiles.forEach((componentFile) => { + // const componentName = componentFile.split('/').pop() + const componentFileName = componentFile.split('/').pop().replace('.component.ts', ''); + const componentName = componentFileName.replace(/(?:^|-)([a-z])/g, (_, char) => char.toUpperCase()); + const readmeFilePath = componentFile.replace('.ts', '.md'); + const componentContent = fs.readFileSync(componentFile, 'utf8'); + const selectorMatch = componentContent.match(/@Component\({[^]*?selector: ['"]([^'"]+)['"][^]*?}\)/); + const componentSelectorName = selectorMatch[1]; + initializeDynamicTableContent(readmeFilePath); + + if (!componentSelectorName) { + throw new Error(`Unable to find the component name in the file ${componentFileName}`); + } + + // const directiveRegex = new RegExp(`@Directive\\(\\s*{[^}]*selector:\\s*['"]${componentName}\\s*\\[([^'"]+)\\]`, 'g'); + const directiveRegex = /^\s*(selector):\s*(['"])(.*?)\2\s*$/gm; + + directiveFiles.forEach((directiveFile) => { + const directiveContent = fs.readFileSync(directiveFile, 'utf8'); + + let directiveNameMatch; + while ((directiveNameMatch = directiveRegex.exec(directiveContent)) !== null) { + if (directiveNameMatch[0].includes('@Directive({\n//')) { + // Skip directives that are commented out + continue; + } + const selectorValue = directiveNameMatch[3].split(','); + const directiveMatch = selectorValue.find((value) => value.includes(componentSelectorName)); + + if (directiveMatch) { + const directiveName = directiveMatch.match(/\[(.*?)\]/).pop(); + const className = directiveName.replace(/(^\w{1})|(\s+\w{1})/g, (letter) => letter.toUpperCase()) + 'Directive'; + const inputRegex = new RegExp( + `@Input\\(\\)\\s+set\\s+(${directiveName.replace(/\[/g, '\\[').replace(/\]/g, '\\]')})\\((\\w+):\\s+(\\w+)` + ); + const inputMatch = directiveContent.match(inputRegex); + const inputType = inputMatch && inputMatch.pop(); + + if (inputType && className) { + let finalClassName = componentName === 'Videoconference' ? className : componentName + className; + addRowToTable(readmeFilePath, directiveName, inputType, finalClassName); + } + } else { + console.log(`The selector "${componentSelectorName}" does not match with ${selectorValue}. Skipping...`); + } + } + }); + + if (apiTableContentIsEmpty(readmeFilePath)) { + removeApiTableContent(readmeFilePath); + } + }); +} + +// Function to add a row to a Markdown table in a file +function addRowToTable(filePath, parameter, type, reference) { + // Read the current content of the file + try { + const data = fs.readFileSync(filePath, 'utf8'); + + // Define the target line and the Markdown row + const markdownRow = `| **${parameter}** | \`${type}\` | [${reference}](../directives/${reference}.html) |`; + + // Find the line that contains the table + const lines = data.split('\n'); + const targetIndex = lines.findIndex((line) => line.includes(endApiLine)); + + if (targetIndex !== -1) { + // Insert the new row above the target line + lines.splice(targetIndex, 0, markdownRow); + + // Join the lines back together + const updatedContent = lines.join('\n'); + + // Write the updated content to the file + fs.writeFileSync(filePath, updatedContent, 'utf8'); + console.log('Row added successfully.'); + } else { + console.error('Table not found in the file.'); + } + } catch (error) { + console.error('Error writing to file:', error); + } +} + +function replaceDynamicTableContent(filePath, content) { + // Read the current content of the file + try { + const data = fs.readFileSync(filePath, 'utf8'); + const pattern = new RegExp(`${startApiLine}([\\s\\S]*?)${endApiLine}`, 'g'); + + // Replace the content between startLine and endLine with the replacement table + const modifiedContent = data.replace(pattern, (match, capturedContent) => { + return startApiLine + '\n' + content + '\n' + endApiLine; + }); + // Write the modified content back to the file + fs.writeFileSync(filePath, modifiedContent, 'utf8'); + } catch (error) { + if (error.code === 'ENOENT') { + console.log(`${filePath} not found! Maybe it is an internal component. Skipping...`); + } else { + console.error('Error writing to file:', error); + } + } +} + +const directiveFiles = getDirectiveFiles(); +const componentFiles = getComponentFiles(); +const adminFiles = getAdminFiles(); +writeApiDirectivesTable(componentFiles.concat(adminFiles), directiveFiles); diff --git a/openvidu-components-angular/projects/openvidu-components-angular/doc/scripts/generate-directive-tutorials.js b/openvidu-components-angular/projects/openvidu-components-angular/doc/scripts/generate-directive-tutorials.js new file mode 100644 index 00000000..fe76a085 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/doc/scripts/generate-directive-tutorials.js @@ -0,0 +1,75 @@ +const fs = require('fs'); + +// Tutorial paths as key-value pairs +const tutorialPaths = { + ovToolbar: '../../openvidu-livekit-tutorials/openvidu-components/openvidu-custom-toolbar/src/app/app.component.ts', + ovToolbarAdditionalButtons: '../../openvidu-livekit-tutorials/openvidu-components/openvidu-toolbar-buttons/src/app/app.component.ts', + ovToolbarAdditionalPanelButtons: + '../../openvidu-livekit-tutorials/openvidu-components/openvidu-toolbar-panel-buttons/src/app/app.component.ts', + ovPanel: '../../openvidu-livekit-tutorials/openvidu-components/openvidu-custom-panels/src/app/app.component.ts', + ovAdditionalPanels: '../../openvidu-livekit-tutorials/openvidu-components/openvidu-additional-panels/src/app/app.component.ts', + ovChatPanel: '../../openvidu-livekit-tutorials/openvidu-components/openvidu-custom-chat-panel/src/app/app.component.ts', + ovActivitiesPanel: '../../openvidu-livekit-tutorials/openvidu-components/openvidu-custom-activities-panel/src/app/app.component.ts', + ovParticipantsPanel: '../../openvidu-livekit-tutorials/openvidu-components/openvidu-custom-participants-panel/src/app/app.component.ts', + ovParticipantPanelItem: + '../../openvidu-livekit-tutorials/openvidu-components/openvidu-custom-participant-panel-item/src/app/app.component.ts', + ovParticipantPanelItemElements: + '../../openvidu-livekit-tutorials/openvidu-components/openvidu-custom-participant-panel-item-elements/src/app/app.component.ts', + ovLayout: '../../openvidu-livekit-tutorials/openvidu-components/openvidu-custom-layout/src/app/app.component.ts', + ovStream: '../../openvidu-livekit-tutorials/openvidu-components/openvidu-custom-stream/src/app/app.component.ts' +}; + +// Path of the file where to copy the text +const targetFile = 'projects/openvidu-components-angular/src/lib/directives/template/openvidu-components-angular.directive.ts'; + +// Function to read a file based on the path and return its content +function readFile(path) { + try { + return fs.readFileSync(path, 'utf-8'); + } catch (error) { + console.error(`Error reading the file ${path}: ${error.message}`); + return null; + } +} + +// Function to write content between two specific lines in the specified file +function writeContentToFile(file, startLine, endLine, content) { + try { + const lines = fs.readFileSync(file, 'utf-8').split('\n'); + const startIndex = lines.findIndex((line) => line.includes(startLine)); + let endIndex = lines.findIndex((line) => line.includes(endLine)); + + if (startIndex !== -1 && endIndex !== -1) { + const codeBlock = ['* ```typescript', ...content.split('\n').map((line) => `* ${line}`), '* ```']; + // Remove lines between startLine and endLine + lines.splice(startIndex + 1, endIndex - startIndex - 1); + + // Update the end index + endIndex = lines.findIndex((line) => line.includes(endLine)); + + // Insert the new code block + lines.splice(endIndex, 0, ...codeBlock); + + fs.writeFileSync(file, lines.join('\n')); + console.log(`Content added successfully to ${file}`); + } else { + console.error(`The specified lines were not found in ${file}`); + } + } catch (error) { + console.error(`Error writing to the file ${file}: ${error.message}`); + } +} + +// Iterate over the tutorialPaths object and copy content to targetFile +for (const key in tutorialPaths) { + if (tutorialPaths.hasOwnProperty(key)) { + const tutorialPath = tutorialPaths[key]; + const tutorialContent = readFile(tutorialPath); + const startLine = ``; + const endLine = ``; + + if (tutorialContent) { + writeContentToFile(targetFile, startLine, endLine, tutorialContent); + } + } +} diff --git a/openvidu-components-angular/projects/openvidu-angular/doc/style.css b/openvidu-components-angular/projects/openvidu-components-angular/doc/styles/style.css similarity index 68% rename from openvidu-components-angular/projects/openvidu-angular/doc/style.css rename to openvidu-components-angular/projects/openvidu-components-angular/doc/styles/style.css index 6b967387..3287e7cd 100644 --- a/openvidu-components-angular/projects/openvidu-angular/doc/style.css +++ b/openvidu-components-angular/projects/openvidu-components-angular/doc/styles/style.css @@ -13,16 +13,39 @@ display: none; } */ -.token.property, .token.tag, .token.constant, .token.symbol, .token.deleted { - color: rgb(0, 204, 255); - +.language-typescript, .token.attr-name { + color: #b6e8ff !important; } -.token.selector, .token.attr-name, .token.string, .token.char, .token.builtin, .token.inserted { - color: #d3ff7c; +.token.keyword { + color: rgb(165, 119, 196); } -.token.atrule, .token.attr-value, .token.function, .token.class-name { - color: rgb(255, 170, 72); +.token.punctuation { + color: #ebca5e; +} + +.token.constant, .token.tag { + color: #58b4ff; +} + +.token.function { + color: #f3d398; +} + +.language-html .token.punctuation.attr-equals{ + color: #fff !important; +} + +.token.tag .token.punctuation { + color: #dadada; +} + +.token.string, .token.attr-value, .token.attr-value .punctuation { + color: #dfa178; +} + +.token.builtin, .token.atrule, .token.class-name { + color: #61d6a0; } .italic { @@ -38,8 +61,6 @@ div>p { } code { - - background-color: rgb(233, 245, 255); font-weight: bold; padding: 2px; } @@ -49,7 +70,7 @@ code { nav a[href*="properties.html"], nav a[href*="overview.html"], nav a[href*="index.html"], - section[data-compodoc="block-accessors"] { +.component .tab-pane section[data-compodoc="block-accessors"] { display: none !important; } @@ -57,18 +78,6 @@ code { min-height: 0; } -.custom-table-container { - display: flex; -} - -.custom-table-container table { - margin: auto; -} - -.custom-table-container>div { - padding: 10px; -} - .warn-container, .info-container { border-radius: 5px; width: 100%; diff --git a/openvidu-components-angular/projects/openvidu-angular/doc/tsconfig.doc.json b/openvidu-components-angular/projects/openvidu-components-angular/doc/tsconfig.doc.json similarity index 72% rename from openvidu-components-angular/projects/openvidu-angular/doc/tsconfig.doc.json rename to openvidu-components-angular/projects/openvidu-components-angular/doc/tsconfig.doc.json index ecdeeded..dd5ca432 100644 --- a/openvidu-components-angular/projects/openvidu-angular/doc/tsconfig.doc.json +++ b/openvidu-components-angular/projects/openvidu-components-angular/doc/tsconfig.doc.json @@ -6,11 +6,12 @@ "../src/lib/services/**/*.ts", "../src/lib/models/**/*.ts", "../src/lib/pipes/**/*.ts", - "../../../src/app/openvidu-webcomponent/**/*.ts", + // "../../../src/app/openvidu-webcomponent/**/*.ts", ], "exclude": [ "src/test.ts", "../src/lib/**/*.mock.ts", "../src/lib/**/*.spec.ts", + "projects/openvidu-components-angular/doc/scripts/**/*.js", ] } \ No newline at end of file diff --git a/openvidu-components-angular/projects/openvidu-angular/karma.conf.js b/openvidu-components-angular/projects/openvidu-components-angular/karma.conf.js similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/karma.conf.js rename to openvidu-components-angular/projects/openvidu-components-angular/karma.conf.js diff --git a/openvidu-components-angular/projects/openvidu-angular/ng-package.json b/openvidu-components-angular/projects/openvidu-components-angular/ng-package.json similarity index 69% rename from openvidu-components-angular/projects/openvidu-angular/ng-package.json rename to openvidu-components-angular/projects/openvidu-components-angular/ng-package.json index 4ce4f92f..baf36d45 100644 --- a/openvidu-components-angular/projects/openvidu-angular/ng-package.json +++ b/openvidu-components-angular/projects/openvidu-components-angular/ng-package.json @@ -1,6 +1,6 @@ { "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", - "dest": "../../dist/openvidu-angular", + "dest": "../../dist/openvidu-components-angular", "lib": { "entryFile": "src/public-api.ts" } diff --git a/openvidu-components-angular/projects/openvidu-components-angular/package.json b/openvidu-components-angular/projects/openvidu-components-angular/package.json new file mode 100644 index 00000000..913a8882 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/package.json @@ -0,0 +1,18 @@ +{ + "dependencies": { + "tslib": "^2.3.0" + }, + "name": "openvidu-components-angular", + "peerDependencies": { + "@angular/animations": "^17.0.0 || ^18.0.0", + "@angular/common": "^17.0.0 || ^18.0.0", + "@angular/core": "^17.0.0 || ^18.0.0", + "@angular/forms": "^17.0.0 || ^18.0.0", + "@angular/material": "^17.0.0 || ^18.0.0", + "autolinker": "^4.0.0", + "buffer": "^6.0.3", + "livekit-client": "2.1.0", + "@livekit/track-processors": "0.3.2" + }, + "version": "3.0.0-beta1" +} diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.html new file mode 100644 index 00000000..e7eb7d18 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.html @@ -0,0 +1,162 @@ +
+ + {{ 'ADMIN.DASHBOARD_TITLE' | translate }} + + + + + +
+ + + + + + + + + + + + + + + + + +
+
+ {{ 'ADMIN.RECORDINGS.EMPTY' | translate }} +
+ +
+ + +
+ +
+ + + + +
+
+
+
+
+ {{ 'ADMIN.RECORDINGS.NAME' | translate }}{{ recording.filename }} +
+
+ {{ 'ADMIN.RECORDINGS.ROOM' | translate }}{{ recording.roomName }} +
+
+ {{ 'ADMIN.RECORDINGS.OUTPUT' | translate }}{{ recording.outputMode }} +
+
+ {{ 'ADMIN.RECORDINGS.DATE_START' | translate }}{{ recording.startedAt | date: 'M/d/yy, H:mm:ss' }} +
+
+ {{ 'ADMIN.RECORDINGS.DATE_END' | translate }}{{ recording.endedAt | date: 'M/d/yy, H:mm:ss' }} +
+
+ {{ 'ADMIN.RECORDINGS.DURATION' | translate }}{{ recording.duration | duration }} +
+
+ {{ 'ADMIN.RECORDINGS.SIZE' | translate }}{{ recording.size / 1024 / 1024 | number: '1.1-2' }} MB +
+
+ {{ 'ADMIN.RECORDINGS.STATUS' | translate }} + {{ recording.status }} +
+
+
+
+
+
+
+ +
+ +
+
+ + + {{ 'ADMIN.POWERED_BY' | translate }} + OpenVidu + +
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.md b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.md new file mode 100644 index 00000000..4742cb5c --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.md @@ -0,0 +1,8 @@ +## API Directives +With the following directives you can modify the default User Interface with the aim of fully customizing your videoconference application. + + +| **Parameter** | **Type** | **Reference** | +|:--------------------------------: | :-------: | :---------------------------------------------: | +| **recordingsList** | `RecordingInfo` | [AdminDashboardRecordingsListDirective](../directives/AdminDashboardRecordingsListDirective.html) | + \ No newline at end of file diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/dashboard/dashboard.component.css b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.scss similarity index 66% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/admin/dashboard/dashboard.component.css rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.scss index b0e2bbd1..220d2687 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/dashboard/dashboard.component.css +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.scss @@ -7,17 +7,30 @@ background-color: var(--ov-secondary-color); color: var(--ov-text-color); } + +.logout-btn { + color: var(--ov-text-color); +} + .dashboard-body { + background-color: var(--ov-secondary-color); height: calc(100% - 75px); } +.toolbar-spacer { + flex: 1 1 auto; +} #toolbar-search { width: 100%; - height: 40px; + height: 50px; background-color: var(--ov-light-color); padding: 6px; display: flex; align-items: center; + + .searchbar-btn { + padding: 0px; + } } #toolbar-sort-div { @@ -74,15 +87,27 @@ } .recordings-container { - height: calc(100% - 51px); - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + background-color: var(--ov-secondary-color); + height: calc(100% - 50px); overflow-y: auto; overflow-x: hidden; -} + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + grid-auto-rows: max-content; + gap: 15px; + justify-content: center; + align-items: center; + padding: 20px; + padding-bottom: 80px; + box-sizing: border-box; -.recording-card { - background-color: var(--ov-panel-background); + .recording-card { + border-radius: 10px; + display: flex; + justify-content: center; + align-items: center; + background-color: var(--ov-panel-background); + } } .video-div-container { @@ -104,15 +129,6 @@ max-height: 100%; } -.item { - flex-grow: 1; - margin: 10px; -} - -.item + .item { - margin-left: 2%; -} - .video-btns { position: absolute; transform: translate(-50%, -50%); @@ -152,23 +168,65 @@ .video-card-tag { font-size: 13px; color: var(--ov-panel-text-color); + font-weight: bold; } + .video-card-value { float: right; font-size: 13.5px; } +.status-value { + font-weight: bold; +} + +.video-card-value { + padding: 0px 5px; + border-radius: 5px; + + &.READY { + background-color: #8bffc9; + } + + &.STARTED { + background-color: #ffcc00; + } + + &.FAILED { + background-color: #ff6666; + } +} + +.load-more-container { + position: absolute; + left: 50%; + bottom: 35px; + transform: translateX(-50%); + + .load-more-btn { + background-color: var(--ov-panel-background); + color: var(--ov-panel-text-color); + border-radius: var(--ov-panel-radius); + } +} + .footer { height: 25px; - background-color: var(--ov-secondary-color); + background-color: var(--ov-primary-color); color: var(--ov-text-color); position: absolute; bottom: 0; left: 0; font-size: 12px; + + display: inline-block; + flex-direction: column; + align-items: stretch; + gap: 2px; } .footer a { color: var(--ov-tertiary-color); + padding-left: 5px; } .no-recordings-warn { @@ -176,6 +234,7 @@ width: 100%; display: table; text-align: center; + color: var(--ov-text-color); } /* TODO(mdc-migration): The following rule targets internal classes of form-field that may no longer apply for the MDC version. */ ::ng-deep .mat-form-field-appearance-fill .mat-form-field-flex { @@ -187,3 +246,21 @@ ::ng-deep .mat-form-field-wrapper { height: 100% !important; } + +@media screen and (max-width: 599px) { + .footer { + flex-direction: row; + } +} + +@media (max-width: 600px) { + .grid-container { + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + } + } + + @media (min-width: 601px) and (max-width: 900px) { + .grid-container { + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + } + } diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/dashboard/dashboard.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.spec.ts similarity index 58% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/admin/dashboard/dashboard.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.spec.ts index 5ec4ff8f..6217b8d2 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/dashboard/dashboard.component.spec.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.spec.ts @@ -1,20 +1,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { DashboardComponent } from './dashboard.component'; +import { AdminDashboardComponent } from './admin-dashboard.component'; describe('DashboardComponent', () => { - let component: DashboardComponent; - let fixture: ComponentFixture; + let component: AdminDashboardComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ DashboardComponent ] + declarations: [ AdminDashboardComponent ] }) .compileComponents(); }); beforeEach(() => { - fixture = TestBed.createComponent(DashboardComponent); + fixture = TestBed.createComponent(AdminDashboardComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.ts new file mode 100644 index 00000000..cee6e804 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-dashboard/admin-dashboard.component.ts @@ -0,0 +1,259 @@ +import { Component, OnInit, Output, EventEmitter, OnDestroy } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { RecordingDeleteRequestedEvent, RecordingInfo, RecordingStatus } from '../../models/recording.model'; +import { ActionService } from '../../services/action/action.service'; +import { OpenViduComponentsConfigService } from '../../services/config/openvidu-components-angular.config.service'; +import { RecordingService } from '../../services/recording/recording.service'; + +@Component({ + selector: 'ov-admin-dashboard', + templateUrl: './admin-dashboard.component.html', + styleUrls: ['./admin-dashboard.component.scss'] +}) +export class AdminDashboardComponent implements OnInit, OnDestroy { + /** + * Provides event notifications that fire when delete recording button has been clicked. + * The recording should be deleted using the REST API. + * @param recordingId + */ + @Output() onRecordingDeleteRequested: EventEmitter = new EventEmitter(); + + /** + * Provides event notifications that fire when refresh recordings button has been clicked. + * The recordings should be updated using the REST API. + */ + @Output() onRefreshRecordingsRequested: EventEmitter = new EventEmitter(); + + /** + * Provides event notifications that fire when load more button has been clicked. + * The recordings should be updated using the REST API with the continuation token. + */ + @Output() onLoadMoreRecordingsRequested: EventEmitter = new EventEmitter(); + + /** + * Provides event notifications that fire when logout button has been clicked. + */ + @Output() onLogoutRequested: EventEmitter = new EventEmitter(); + + /** + * @internal + */ + recordings: RecordingInfo[] = []; + /** + * @internal + */ + sortDescendent = true; + /** + * @internal + */ + sortByLegend = 'Sort by'; + /** + * @internal + */ + searchValue = ''; + /** + * @internal + */ + recordingStatusEnum = RecordingStatus; + private adminSubscription: Subscription; + /** + * @internal + */ + constructor( + private actionService: ActionService, + private recordingService: RecordingService, + private libService: OpenViduComponentsConfigService + ) {} + + /** + * @internal + */ + ngOnInit(): void { + this.subscribeToAdminDirectives(); + } + + /** + * @internal + */ + ngOnDestroy() { + if (this.adminSubscription) this.adminSubscription.unsubscribe(); + } + + /** + * @internal + */ + logout() { + this.onLogoutRequested.emit(); + } + + /** + * @internal + */ + refreshRecordings() { + this.recordings = []; + this.onRefreshRecordingsRequested.emit(); + } + + /** + * @internal + */ + sortRecordingsByDateStart() { + this.recordings.sort((a, b) => { + if (!a.startedAt || !b.startedAt) return 0; + if (a.startedAt > b.startedAt) { + return this.sortDescendent ? -1 : 1; + } else if (a.startedAt < b.startedAt) { + return this.sortDescendent ? 1 : -1; + } else { + return 0; + } + }); + this.sortByLegend = 'Start Date'; + } + + /** + * @internal + */ + sortRecordingsByDateEnd() { + this.recordings.sort((a, b) => { + if (!a.endedAt || !b.endedAt) return 0; + if (a.endedAt > b.endedAt) { + return this.sortDescendent ? -1 : 1; + } else if (a.endedAt < b.endedAt) { + return this.sortDescendent ? 1 : -1; + } else { + return 0; + } + }); + this.sortByLegend = 'End Date'; + } + + /** + * @internal + */ + sortRecordingsByDuration() { + this.recordings.sort((a, b) => { + if (!a.duration || !b.duration) return 0; + if (a.duration > b.duration) { + return this.sortDescendent ? -1 : 1; + } else if (a.duration < b.duration) { + return this.sortDescendent ? 1 : -1; + } else { + return 0; + } + }); + this.sortByLegend = 'Duration'; + } + + /** + * @internal + */ + sortRecordingsBySize() { + this.recordings.sort((a, b) => { + if (!a.size || !b.size) return 0; + if (a.size > b.size) { + return this.sortDescendent ? -1 : 1; + } else if (a.size < b.size) { + return this.sortDescendent ? 1 : -1; + } else { + return 0; + } + }); + this.sortByLegend = 'Size'; + } + + /** + * @internal + */ + loadMore(event: any) { + if (event) { + this.onLoadMoreRecordingsRequested.emit(); + } + } + + /** + * @internal + */ + deleteRecording(recording: RecordingInfo) { + const succsessCallback = async () => { + if (!recording.id) { + throw new Error('Error deleting recording. Recording id is undefined'); + } + const payload: RecordingDeleteRequestedEvent = { + roomName: recording.roomName, + recordingId: recording.id + }; + this.onRecordingDeleteRequested.emit(payload); + //mark the recording as deleted + recording.markedForDeletion = true; + }; + + this.actionService.openDeleteRecordingDialog(succsessCallback); + } + + /** + * @internal + */ + download(recording: RecordingInfo) { + this.recordingService.downloadRecording(recording); + } + + /** + * @internal + */ + async play(recording: RecordingInfo) { + this.recordingService.playRecording(recording); + } + + /** + * @internal + */ + trackByRecordingId(index: number, recording: any): any { + return recording.id; + } + + private filterDeletedRecordings(recordings: RecordingInfo[]) { + this.recordings = this.recordings.filter( + (recording) => !recording.markedForDeletion && recordings.some((r) => r.id === recording.id) + ); + } + + private mergeRecordings(recordings: RecordingInfo[]) { + const recordingMap = new Map(this.recordings.map((recording) => [recording.id, recording])); + recordings.forEach((recording) => recordingMap.set(recording.id, recording)); + this.recordings = Array.from(recordingMap.values()); + } + + private sortRecordings() { + switch (this.sortByLegend) { + case 'End Date': + this.sortRecordingsByDateEnd(); + break; + case 'Start Date': + this.sortRecordingsByDateStart(); + break; + case 'Duration': + this.sortRecordingsByDuration(); + break; + case 'Size': + this.sortRecordingsBySize(); + break; + default: + this.sortRecordingsByDateEnd(); + break; + } + } + + private subscribeToAdminDirectives() { + this.adminSubscription = this.libService.adminRecordingsList$.subscribe((recordings: RecordingInfo[]) => { + + // Remove the recordings that are marked for deletion + this.filterDeletedRecordings(recordings); + + // Merge the new recordings and avoid duplicates + this.mergeRecordings(recordings); + + this.sortRecordings(); + }); + } +} diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.html new file mode 100644 index 00000000..91cfa628 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.html @@ -0,0 +1,37 @@ + {{ 'ADMIN.DASHBOARD_TITLE' | translate }} + +
+
+
+
+ +
+
+
+ + + +
+ + {{ 'ADMIN.USERNAME' | translate }} + + + {{ 'ADMIN.USERNAME_REQUIRED' | translate }} + + + + {{ 'ADMIN.PASSWORD' | translate }} + + + {{ 'ADMIN.PASSWORD_REQUIRED' | translate }} + + +
+ +
+
+
+
+
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.md b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.md new file mode 100644 index 00000000..f458340a --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.md @@ -0,0 +1,9 @@ + +## API Directives +With the following directives you can modify the default User Interface with the aim of fully customizing your videoconference application. + + +| **Parameter** | **Type** | **Reference** | +|:--------------------------------: | :-------: | :---------------------------------------------: | +| **error** | `any` | [AdminLoginErrorDirective](../directives/AdminLoginErrorDirective.html) | + \ No newline at end of file diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.scss b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.scss new file mode 100644 index 00000000..3494f635 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.scss @@ -0,0 +1,37 @@ +.header { + height: 50px; + background-color: var(--ov-primary-color); + color: var(--ov-text-color); +} + +.center-container { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background-color: var(--ov-secondary-color); +} + +.card-container { + text-align: center; + background-color: var(--ov-panel-background); + padding: 10px; + max-width: 300px; + min-width: 300px; + border-radius: 10px; + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); +} + +.form-btn { + background-color: var(--ov-tertiary-color) !important; + color: var(--ov-text-color) !important; +} + +.form-field, +.form-btn { + width: 100%; +} + +::ng-deep .mat-mdc-progress-spinner { + --mdc-circular-progress-active-indicator-color: var(--ov-tertiary-color); +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/admin/login/login.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.spec.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/admin/login/login.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.spec.ts diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.ts new file mode 100644 index 00000000..4ede4ee6 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/admin/admin-login/admin-login.component.ts @@ -0,0 +1,87 @@ +import { Component, EventEmitter, OnInit, Output } from '@angular/core'; +import { Validators, FormGroup, FormBuilder } from '@angular/forms'; +import { Subscription } from 'rxjs'; +import { ActionService } from '../../services/action/action.service'; +import { OpenViduComponentsConfigService } from '../../services/config/openvidu-components-angular.config.service'; + +@Component({ + selector: 'ov-admin-login', + templateUrl: './admin-login.component.html', + styleUrls: ['./admin-login.component.scss'] +}) +export class AdminLoginComponent implements OnInit { + /** + * Provides event notifications that fire when login button has been clicked. + * The event will contain the credentials value. + * @returns {EventEmitter<{ username: string; password: string }>} + */ + @Output() onLoginRequested: EventEmitter<{ username: string; password: string }> = new EventEmitter<{ + username: string; + password: string; + }>(); + + /** + * @internal + */ + loading = false; + + /** + * @internal + */ + showSpinner = false; + + /** + * @internal + */ + loginForm: FormGroup; + + private errorSub: Subscription; + + /** + * @internal + */ + constructor( + private libService: OpenViduComponentsConfigService, + private actionService: ActionService, + private fb: FormBuilder + ) { + this.loginForm = this.fb.group({ + username: ['', [Validators.required, Validators.minLength(4)]], + password: ['', [Validators.required, Validators.minLength(4)]] + }); + } + + /** + * @internal + */ + ngOnInit() { + this.subscribeToAdminLoginDirectives(); + } + + /** + * @internal + */ + ngOnDestroy() { + this.showSpinner = false; + if (this.errorSub) this.errorSub.unsubscribe(); + } + + /** + * @internal + */ + login() { + if (this.loginForm.invalid) return; + this.showSpinner = true; + this.onLoginRequested.emit(this.loginForm.value); + } + + private subscribeToAdminLoginDirectives() { + this.errorSub = this.libService.adminLoginError$.subscribe((value) => { + const errorExists = !!value; + if (errorExists) { + this.showSpinner = false; + this.actionService.openDialog(value.error, value.message, true); + } + }); + } +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.css b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/audio-wave/audio-wave.component.scss similarity index 94% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.css rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/audio-wave/audio-wave.component.scss index 877ed18a..5807569a 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.css +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/audio-wave/audio-wave.component.scss @@ -29,7 +29,7 @@ max-height: 15px; height: 15px; width: 15px; - border-radius: var(--ov-buttons-radius); + border-radius: var(--ov-video-radius); display: flex; justify-content: space-between; } diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/audio-wave/audio-wave.component.spec.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/audio-wave/audio-wave.component.spec.ts diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/audio-wave/audio-wave.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/audio-wave/audio-wave.component.ts new file mode 100644 index 00000000..815c4ff5 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/audio-wave/audio-wave.component.ts @@ -0,0 +1,15 @@ +import { Component } from '@angular/core'; + +/** + * @internal + */ +@Component({ + selector: 'ov-audio-wave', + template: `
+
+
+
+
`, + styleUrls: ['./audio-wave.component.scss'] +}) +export class AudioWaveComponent {} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/avatar-profile/avatar-profile.component.css b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/avatar-profile/avatar-profile.component.scss similarity index 89% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/avatar-profile/avatar-profile.component.css rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/avatar-profile/avatar-profile.component.scss index 1bb48b8c..5277e5c6 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/avatar-profile/avatar-profile.component.css +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/avatar-profile/avatar-profile.component.scss @@ -3,7 +3,8 @@ width: 100%; background-color: #000000; position: absolute; - z-index: 999; + z-index: 888; + border-radius: var(--ov-video-radius); } .initial { diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/avatar-profile/avatar-profile.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/avatar-profile/avatar-profile.component.spec.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/avatar-profile/avatar-profile.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/avatar-profile/avatar-profile.component.spec.ts diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/avatar-profile/avatar-profile.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/avatar-profile/avatar-profile.component.ts similarity index 79% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/avatar-profile/avatar-profile.component.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/avatar-profile/avatar-profile.component.ts index facfa7be..5cb015f3 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/avatar-profile/avatar-profile.component.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/avatar-profile/avatar-profile.component.ts @@ -13,14 +13,14 @@ import { Component, Input } from '@angular/core'; `, - styleUrls: ['./avatar-profile.component.css'] + styleUrls: ['./avatar-profile.component.scss'] }) export class AvatarProfileComponent { letter: string; @Input() - set name(nickname: string) { - this.letter = nickname[0]; + set name(name: string) { + if (name) this.letter = name[0]; } @Input() color; diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/captions/captions.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/captions/captions.component.html similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/captions/captions.component.html rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/captions/captions.component.html diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/captions/captions.component.css b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/captions/captions.component.scss similarity index 87% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/captions/captions.component.css rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/captions/captions.component.scss index c5ff384e..5306efe9 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/captions/captions.component.css +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/captions/captions.component.scss @@ -35,13 +35,16 @@ mat-spinner { position: relative; - top: 35%; - bottom: 0; - left: 0; - right: 0; - margin: auto; + top: 35%; + bottom: 0; + left: 0; + right: 0; + margin: auto; } +::ng-deep .mat-mdc-progress-spinner { + --mdc-circular-progress-active-indicator-color: var(--ov-tertiary-color); +} /* * Screen XL @@ -50,7 +53,7 @@ mat-spinner { padding: 0px 14vw; } -.captions-center-container.screen-xl .caption-event>span { +.captions-center-container.screen-xl .caption-event > span { font-size: 22px; } @@ -71,11 +74,10 @@ mat-spinner { max-height: 35px; } - /* * Screen MD */ -.captions-center-container.screen-md .caption-event>span { +.captions-center-container.screen-md .caption-event > span { font-size: 20px; } @@ -103,7 +105,7 @@ mat-spinner { .captions-center-container.screen-sm { padding: 0px 2vw; } -.captions-center-container.screen-sm .caption-event>span { +.captions-center-container.screen-sm .caption-event > span { font-size: 20px; } @@ -133,7 +135,7 @@ mat-spinner { padding: 0px 2vw 0px; } -.captions-center-container.screen-xs .caption-event>span { +.captions-center-container.screen-xs .caption-event > span { font-size: 20px; } .captions-center-container.screen-xs #speaker { @@ -153,12 +155,10 @@ mat-spinner { max-height: 35px; } -.captions-center-container .going-to-disappear{ +.captions-center-container .going-to-disappear { max-height: 30px !important; } - - #caption-settings-btn { color: var(--ov-text-color); background-color: var(--ov-secondary-color); @@ -188,7 +188,6 @@ mat-spinner { pointer-events: none; } - .caption-text, #speaker { color: var(--ov-text-color); @@ -202,8 +201,6 @@ mat-spinner { word-break: break-word; } - - ::-webkit-scrollbar { display: none; } diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/captions/captions.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/captions/captions.component.spec.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/captions/captions.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/captions/captions.component.spec.ts diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/captions/captions.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/captions/captions.component.ts similarity index 67% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/captions/captions.component.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/captions/captions.component.ts index e25a7073..1db5806a 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/captions/captions.component.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/captions/captions.component.ts @@ -3,9 +3,8 @@ import { Subscription } from 'rxjs'; import { PanelService } from '../../services/panel/panel.service'; import { animate, style, transition, trigger } from '@angular/animations'; -import { Session, SpeechToTextEvent } from 'openvidu-browser-v2compatibility'; import { CaptionModel, CaptionsLangOption } from '../../models/caption.model'; -import { PanelEvent, PanelSettingsOptions, PanelType } from '../../models/panel.model'; +import { PanelStatusInfo, PanelSettingsOptions, PanelType } from '../../models/panel.model'; import { CaptionService } from '../../services/caption/caption.service'; import { OpenViduService } from '../../services/openvidu/openvidu.service'; import { ParticipantService } from '../../services/participant/participant.service'; @@ -16,7 +15,7 @@ import { ParticipantService } from '../../services/participant/participant.servi @Component({ selector: 'ov-captions', templateUrl: './captions.component.html', - styleUrls: ['./captions.component.css'], + styleUrls: ['./captions.component.scss'], animations: [ trigger('captionAnimation', [ transition(':enter', [style({ opacity: 0 }), animate('50ms ease-in', style({ opacity: 1 }))]) @@ -41,7 +40,7 @@ export class CaptionsComponent implements OnInit { captionEvents: CaptionModel[] = []; - session: Session; + // session: Session; isSttReady: boolean = true; private deleteFirstTimeout: NodeJS.Timeout; @@ -55,7 +54,6 @@ export class CaptionsComponent implements OnInit { private panelTogglingSubscription: Subscription; private sttStatusSubscription: Subscription; - constructor( private panelService: PanelService, private openviduService: OpenViduService, @@ -65,27 +63,23 @@ export class CaptionsComponent implements OnInit { ) {} async ngOnInit(): Promise { - this.subscribeToSTTStatus(); - this.captionService.setCaptionsEnabled(true); - this.captionLangSelected = this.captionService.getLangSelected(); - this.session = this.openviduService.getWebcamSession(); - - await this.openviduService.subscribeRemotesToSTT(this.captionLangSelected.lang); - - this.subscribeToCaptionLanguage(); - this.subscribeToPanelToggling(); - this.subscribeToTranscription(); + // this.subscribeToSTTStatus(); + // this.captionService.setCaptionsEnabled(true); + // this.captionLangSelected = this.captionService.getLangSelected(); + // await this.openviduService.subscribeRemotesToSTT(this.captionLangSelected.lang); + // this.subscribeToCaptionLanguage(); + // this.subscribeToPanelToggling(); + // this.subscribeToTranscription(); } async ngOnDestroy() { - await this.openviduService.unsubscribeRemotesFromSTT(); - this.captionService.setCaptionsEnabled(false); - if (this.screenSizeSub) this.screenSizeSub.unsubscribe(); - if (this.panelTogglingSubscription) this.panelTogglingSubscription.unsubscribe(); - if(this.sttStatusSubscription) this.sttStatusSubscription.unsubscribe(); - this.session.off('speechToTextMessage'); - this.captionEvents = []; - + // await this.openviduService.unsubscribeRemotesFromSTT(); + // this.captionService.setCaptionsEnabled(false); + // if (this.screenSizeSub) this.screenSizeSub.unsubscribe(); + // if (this.panelTogglingSubscription) this.panelTogglingSubscription.unsubscribe(); + // if(this.sttStatusSubscription) this.sttStatusSubscription.unsubscribe(); + // // this.session.off('speechToTextMessage'); + // this.captionEvents = []; } onSettingsCliked() { @@ -93,26 +87,25 @@ export class CaptionsComponent implements OnInit { } private subscribeToTranscription() { - this.session.on('speechToTextMessage', (event: SpeechToTextEvent) => { - if(!!event.text) { - clearInterval(this.deleteAllTimeout); - const { connectionId, data } = event.connection; - const nickname: string = this.participantService.getNicknameFromConnectionData(data); - const color = this.participantService.getRemoteParticipantByConnectionId(connectionId)?.colorProfile || ''; - - const caption: CaptionModel = { - connectionId, - nickname, - color, - text: event.text, - type: event.reason - }; - this.updateCaption(caption); - // Delete all events when there are no more events for a period of time - this.deleteAllEventsAfterDelay(this.DELETE_TIMEOUT); - this.cd.markForCheck(); - } - }); + // this.session.on('speechToTextMessage', (event: SpeechToTextEvent) => { + // if(!!event.text) { + // clearInterval(this.deleteAllTimeout); + // const { connectionId, data } = event.connection; + // const nickname: string = this.participantService.getNicknameFromConnectionData(data); + // const color = this.participantService.getRemoteParticipantByConnectionId(connectionId)?.colorProfile || ''; + // const caption: CaptionModel = { + // connectionId, + // nickname, + // color, + // text: event.text, + // type: event.reason + // }; + // this.updateCaption(caption); + // // Delete all events when there are no more events for a period of time + // this.deleteAllEventsAfterDelay(this.DELETE_TIMEOUT); + // this.cd.markForCheck(); + // } + // }); } private updateCaption(caption: CaptionModel): void { let captionEventsCopy = [...this.captionEvents]; @@ -188,26 +181,26 @@ export class CaptionsComponent implements OnInit { }, timeout); } - private subscribeToSTTStatus() { - this.sttStatusSubscription = this.openviduService.isSttReadyObs.subscribe((ready: boolean) => { - this.isSttReady = ready; - this.cd.markForCheck(); - }); - } + // private subscribeToSTTStatus() { + // this.sttStatusSubscription = this.openviduService.isSttReadyObs.subscribe((ready: boolean) => { + // this.isSttReady = ready; + // this.cd.markForCheck(); + // }); + // } - private subscribeToCaptionLanguage() { - this.captionLanguageSubscription = this.captionService.captionLangObs.subscribe((langOpt) => { - this.captionLangSelected = langOpt; - this.cd.markForCheck(); - }); - } + // private subscribeToCaptionLanguage() { + // this.captionLanguageSubscription = this.captionService.captionLangObs.subscribe((langOpt) => { + // this.captionLangSelected = langOpt; + // this.cd.markForCheck(); + // }); + // } - private subscribeToPanelToggling() { - this.panelTogglingSubscription = this.panelService.panelOpenedObs.subscribe((ev: PanelEvent) => { - this.settingsPanelOpened = ev.opened; - setTimeout(() => this.cd.markForCheck(), 300); - }); - } + // private subscribeToPanelToggling() { + // this.panelTogglingSubscription = this.panelService.panelStatusObs.subscribe((ev: PanelStatusInfo) => { + // this.settingsPanelOpened = ev.opened; + // setTimeout(() => this.cd.markForCheck(), 300); + // }); + // } private scrollToBottom(): void { setTimeout(() => { diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/dialogs/delete-recording.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/dialogs/delete-recording.component.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/dialogs/delete-recording.component.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/dialogs/delete-recording.component.ts diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/dialogs/dialog.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/dialogs/dialog.component.ts similarity index 89% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/dialogs/dialog.component.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/dialogs/dialog.component.ts index eadad33d..40a4453a 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/dialogs/dialog.component.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/dialogs/dialog.component.ts @@ -10,7 +10,7 @@ import { DialogData } from '../../models/dialog.model'; selector: 'ov-dialog-template', template: `

{{ data.title }}

-
{{ data.description }}
+
{{ data.description }}
diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/dialogs/pro-feature-dialog.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/dialogs/pro-feature-dialog.component.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/dialogs/pro-feature-dialog.component.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/dialogs/pro-feature-dialog.component.ts diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/dialogs/recording-dialog.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/dialogs/recording-dialog.component.ts new file mode 100644 index 00000000..00f15348 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/dialogs/recording-dialog.component.ts @@ -0,0 +1,46 @@ +import { Component, ElementRef, Inject, ViewChild } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { RecordingDialogData } from '../../models/dialog.model'; + +/** + * @internal + */ +@Component({ + selector: 'app-recording-dialog', + template: ` +
+ +
+
+ +
+ `, + styles: [ + ` + video { + max-height: 64vh; + max-width: 100%; + } + ` + ] +}) +export class RecordingDialogComponent { + @ViewChild('videoElement', { static: true }) videoElement: ElementRef; + + src: string; + + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: RecordingDialogData + ) { + this.src = data.src; + } + close() { + this.dialogRef.close({ manageError: false, error: null }); + } + + handleError() { + const videoElement = this.videoElement.nativeElement; + this.dialogRef.close({ manageError: true, error: videoElement.error }); + } +} diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.html new file mode 100644 index 00000000..c2f734ca --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.html @@ -0,0 +1,37 @@ +
+
+
+ +
+ +
+ +
+
+ + +
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.md b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.md new file mode 100644 index 00000000..993ec568 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.md @@ -0,0 +1,26 @@ + + + +## Structural Directives + +| **Directive** | **Reference** | +| :------------: | :-------------------------------------------------: | +| **\*ovLayout** | [LayoutDirective](../directives/LayoutDirective.html) | + +It is also providing us a way to **replace the {@link StreamComponent Stream Component}** (_which is hosted inside of it_) with a custom one. +It will recognise the following directive in a child element. + +| **Directive** | **Reference** | +| :------------: | :-------------------------------------------------: | +| **\*ovStream** | [StreamDirective](../directives/StreamDirective.html) | + + + +## API Directives + +With the following directives you can modify the default User Interface with the aim of fully customizing your videoconference application. + + +_No API directives available for this component_. + + diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/layout/layout.component.css b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.scss similarity index 50% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/layout/layout.component.css rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.scss index fb640f21..56a2df80 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/layout/layout.component.css +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.scss @@ -1,5 +1,6 @@ -#remote-participant { - height: 100%; +.remote-participant { + height: -webkit-fill-available; + height: -moz-available; } .container { @@ -8,6 +9,11 @@ .withCaptions { height: calc(100% - var(--ov-captions-height, 250px)) !important; } +.withMargin { + margin: 0px 5px; + max-height: calc(100% - 5px) !important; + height: calc(100% - 5px) !important; +} .layout { position: relative; @@ -20,10 +26,11 @@ width: inherit; height: -webkit-fill-available; height: -moz-available; + overflow: none; } -.OT_root, -.OT_root * { +.OV_root, +.OV_root * { color: #ffffff; margin: 0; padding: 0; @@ -32,19 +39,18 @@ vertical-align: baseline; } - -.OT_publisher, -.OT_subscriber { +.OV_publisher, +.OV_subscriber { position: relative; min-width: 0px; min-height: 0px; - padding: 3px; + padding: 1px; transition-duration: 0.1s; transition-timing-function: ease-in-out; } -.OT_publisher .OT_video-element, -.OT_subscriber .OT_video-element { +.OV_publisher .OV_video-element, +.OV_subscriber .OV_video-element { display: block; position: absolute; width: 100%; @@ -52,3 +58,21 @@ -webkit-transform-origin: 0 0; transform-origin: 0 0; } + +.OV_ignored { + display: none; + width: 0px !important; + height: 0px !important; +} + +.OV_minimized { + width: 230px !important; + height: 130px !important; + // position: absolute !important; + bottom: 0 !important; + right: 0 !important; + top: 0 !important; + left: 0 !important; + z-index: 999 !important; + cursor: move; +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/layout/layout.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.spec.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/layout/layout.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.spec.ts diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.ts new file mode 100644 index 00000000..ff122a6b --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/layout/layout.component.ts @@ -0,0 +1,218 @@ +import { + AfterViewInit, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ContentChild, + ElementRef, + OnDestroy, + OnInit, + TemplateRef, + ViewChild, + ViewContainerRef +} from '@angular/core'; +import { Subscription } from 'rxjs'; +import { StreamDirective } from '../../directives/template/openvidu-components-angular.directive'; +import { ParticipantTrackPublication, ParticipantModel } from '../../models/participant.model'; +import { LayoutService } from '../../services/layout/layout.service'; +import { ParticipantService } from '../../services/participant/participant.service'; +import { CdkDrag } from '@angular/cdk/drag-drop'; +import { PanelService } from '../../services/panel/panel.service'; +import { OpenViduComponentsConfigService } from '../../services/config/openvidu-components-angular.config.service'; + +/** + * + * The **LayoutComponent** is hosted inside of the {@link VideoconferenceComponent}. + * It is in charge of displaying the participants streams layout. + */ +@Component({ + selector: 'ov-layout', + templateUrl: './layout.component.html', + styleUrls: ['./layout.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit { + /** + * @ignore + */ + @ContentChild('stream', { read: TemplateRef }) streamTemplate: TemplateRef; + + /** + * @ignore + */ + @ViewChild('layout', { static: false, read: ViewContainerRef }) layoutContainer: ViewContainerRef; + + /** + * @ignore + */ + @ViewChild(CdkDrag) cdkDrag: CdkDrag; + + /** + * @ignore + */ + @ViewChild('localLayoutElement', { static: false, read: ElementRef }) localLayoutElement: ElementRef; + /** + * @ignore + */ + @ContentChild(StreamDirective) + set externalStream(externalStream: StreamDirective) { + // This directive will has value only when STREAM component tagget with '*ovStream' directive + // is inside of the layout component tagged with '*ovLayout' directive + if (externalStream) { + this.streamTemplate = externalStream.template; + } + } + + localParticipant: ParticipantModel | undefined; + remoteParticipants: ParticipantModel[] = []; + /** + * @ignore + */ + captionsEnabled = true; + + private localParticipantSubs: Subscription; + private remoteParticipantsSubs: Subscription; + private captionsSubs: Subscription; + private resizeObserver: ResizeObserver; + private cdkSubscription: Subscription; + private resizeTimeout: NodeJS.Timeout; + private videoIsAtRight: boolean = false; + private lastLayoutWidth: number = 0; + + /** + * @ignore + */ + constructor( + private layoutService: LayoutService, + private panelService: PanelService, + private participantService: ParticipantService, + private libService: OpenViduComponentsConfigService, + private cd: ChangeDetectorRef + ) {} + + ngOnInit(): void { + this.subscribeToParticipants(); + this.subscribeToCaptions(); + } + + ngAfterViewInit() { + this.layoutService.initialize(this.layoutContainer.element.nativeElement); + this.lastLayoutWidth = this.layoutContainer.element.nativeElement.getBoundingClientRect().width; + this.listenToResizeLayout(); + this.listenToCdkDrag(); + } + + ngOnDestroy() { + this.localParticipant = undefined; + this.remoteParticipants = []; + this.resizeObserver?.disconnect(); + this.localParticipantSubs?.unsubscribe(); + this.remoteParticipantsSubs?.unsubscribe(); + this.captionsSubs?.unsubscribe(); + this.cdkSubscription?.unsubscribe(); + this.layoutService.clear(); + } + + /** + * @ignore + */ + trackParticipantElement(_: number, track: ParticipantTrackPublication) { + // This method is used for trackBy in ngFor with the aim of improving performance + // https://angular.io/api/core/TrackByFunction + return track; + } + + private subscribeToCaptions() { + this.captionsSubs = this.layoutService.captionsTogglingObs.subscribe((value: boolean) => { + this.captionsEnabled = value; + this.cd.markForCheck(); + this.layoutService.update(); + }); + } + + private subscribeToParticipants() { + this.localParticipantSubs = this.participantService.localParticipant$.subscribe((p) => { + if (p) { + this.localParticipant = p; + if (!this.localParticipant?.isMinimized) { + this.videoIsAtRight = false; + } + this.layoutService.update(); + this.cd.markForCheck(); + } + }); + + this.remoteParticipantsSubs = this.participantService.remoteParticipants$.subscribe((participants) => { + this.remoteParticipants = participants; + this.layoutService.update(); + this.cd.markForCheck(); + }); + } + + private listenToResizeLayout() { + this.resizeObserver = new ResizeObserver((entries) => { + clearTimeout(this.resizeTimeout); + + this.resizeTimeout = setTimeout(() => { + if (this.localParticipant?.isMinimized) { + const { width: parentWidth } = entries[0].contentRect; + if (this.panelService.isPanelOpened()) { + if (this.lastLayoutWidth < parentWidth) { + // Layout is bigger than before. Maybe the settings panel(wider) has been transitioned to another panel. + if (this.videoIsAtRight) { + this.moveStreamToRight(parentWidth); + } + } else { + // Layout is smaller than before. Emit resize event to update video position. + window.dispatchEvent(new Event('resize')); + const { x, width } = this.cdkDrag.element.nativeElement.getBoundingClientRect(); + this.videoIsAtRight = x + width >= parentWidth; + } + } else { + if (this.videoIsAtRight) { + // Panel is closed and layout has been resized. Video is at right, so move it to right. + this.moveStreamToRight(parentWidth); + } + } + this.lastLayoutWidth = parentWidth; + } + }, 100); + }); + + this.resizeObserver.observe(this.layoutContainer.element.nativeElement); + } + private moveStreamToRight(parentWidth: number) { + const { y, width: elementWidth } = this.cdkDrag.element.nativeElement.getBoundingClientRect(); + const margin = 10; + const newX = parentWidth - elementWidth - margin; + this.cdkDrag.setFreeDragPosition({ x: newX, y }); + } + + private listenToCdkDrag() { + const handler = (event) => { + if (!this.panelService.isPanelOpened()) return; + const { x, width } = this.localLayoutElement.nativeElement.getBoundingClientRect(); + console.log(x); + const { width: parentWidth } = this.layoutContainer.element.nativeElement.getBoundingClientRect(); + if (x === 0) { + // Video is at the left + this.videoIsAtRight = false; + } else if (x + width >= parentWidth) { + // Video is at the right + this.videoIsAtRight = true; + } else { + // Video is in another position + this.videoIsAtRight = false; + } + }; + this.cdkSubscription = this.cdkDrag.released.subscribe(handler); + + if (this.libService.getConfig().production) return; + // Just for allow E2E testing with drag and drop + document.addEventListener('webcomponentTestingEndedDragAndDropEvent', handler); + document.addEventListener('webcomponentTestingEndedDragAndDropRightEvent', (event: any) => { + const { x, y } = event.detail; + this.cdkDrag.setFreeDragPosition({ x, y }); + }); + } +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/video/video.component.css b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/media-element/media-element.component.scss similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/video/video.component.css rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/media-element/media-element.component.scss diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/video/video.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/media-element/media-element.component.spec.ts similarity index 90% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/video/video.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/media-element/media-element.component.spec.ts index 1dc29282..9d26d658 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/video/video.component.spec.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/media-element/media-element.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { VideoComponent } from './video.component'; +import { VideoComponent } from './media-element.component'; describe('VideoComponent', () => { let component: VideoComponent; diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/media-element/media-element.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/media-element/media-element.component.ts new file mode 100644 index 00000000..c5c8d66f --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/media-element/media-element.component.ts @@ -0,0 +1,114 @@ +import { animate, style, transition, trigger } from '@angular/animations'; +import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; +import { Track } from 'livekit-client'; + +/** + * @internal + */ +@Component({ + selector: 'ov-media-element', + template: ` + + + + `, + styleUrls: ['./media-element.component.scss'], + animations: [ + trigger('posterAnimation', [ + transition(':enter', [style({ opacity: 0 }), animate('100ms', style({ opacity: 1 }))]), + transition(':leave', [style({ opacity: 1 }), animate('200ms', style({ opacity: 0 }))]) + ]) + ] +}) +export class MediaElementComponent implements AfterViewInit { + _track: Track; + _videoElement: ElementRef; + _audioElement: ElementRef; + type: Track.Source = Track.Source.Camera; + private _muted: boolean = false; + + @Input() showAvatar: boolean; + @Input() avatarColor: string; + @Input() avatarName: string; + @Input() isLocal: boolean; + + @ViewChild('videoElement', { static: false }) + set videoElement(element: ElementRef) { + this._videoElement = element; + this.attachTracks(); + + } + + @ViewChild('audioElement', { static: false }) + set audioElement(element: ElementRef) { + this._audioElement = element; + this.attachTracks(); + + } + + @Input() + set track(track: Track) { + if (!track) return; + this._track = track; + this.attachTracks(); + } + + @Input() + set muted(muted: boolean) { + this._muted = muted; + if (this._audioElement && !this.isLocal) { + this.muteAudioTrack(this._muted); + } + } + + ngAfterViewInit() { + setTimeout(() => { + if (!this._track) return; + this.attachTracks(); + }); + } + + private updateVideoStyles() { + this.type = this._track.source; + if (this.type === Track.Source.ScreenShare) { + this._videoElement.nativeElement.style.objectFit = 'contain'; + this._videoElement.nativeElement.classList.add('screen-type'); + } else if (this.type === Track.Source.Camera) { + if (this.isLocal) { + this._videoElement.nativeElement.style.transform = 'scaleX(-1)'; + } + this._videoElement.nativeElement.style.objectFit = 'cover'; + this._videoElement.nativeElement.classList.add('camera-type'); + } + } + + private attachTracks() { + if (this.isAudioTrack() && !!this._audioElement && !this.isLocal) { + this.attachAudioTrack(); + } else if (this.isVideoTrack() && !!this._videoElement) { + this.updateVideoStyles(); + this.attachVideoTrack(); + } + } + + private attachVideoTrack() { + this._track.attach(this._videoElement.nativeElement); + } + + private attachAudioTrack() { + this._track.attach(this._audioElement.nativeElement); + this.muteAudioTrack(this._muted); + } + private muteAudioTrack(mute: boolean) { + this._audioElement.nativeElement.muted = mute; + this._track.mediaStreamTrack.enabled = !mute; + } + + private isAudioTrack(): boolean { + return this._track?.kind === Track.Kind.Audio; + } + + private isVideoTrack(): boolean { + return this._track?.kind === Track.Kind.Video; + } +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/activities-panel.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.html similarity index 60% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/activities-panel.component.html rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.html index d470ba51..7291a2b3 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/activities-panel.component.html +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.html @@ -12,17 +12,18 @@ *ngIf="showRecordingActivity" id="recording-activity" [expanded]="expandedPanel === 'recording'" - (onStartRecordingClicked)="_onStartRecordingClicked()" - (onStopRecordingClicked)="_onStopRecordingClicked()" - (onDeleteRecordingClicked)="_onDeleteRecordingClicked($event)" - (onForceRecordingUpdate)="_onForceRecordingUpdate()" + (onRecordingStartRequested)="onRecordingStartRequested.emit($event)" + (onRecordingStopRequested)="onRecordingStopRequested.emit($event)" + (onRecordingDeleteRequested)="onRecordingDeleteRequested.emit($event)" + (onRecordingDownloadClicked)="onRecordingDownloadClicked.emit($event)" + (onRecordingPlayClicked)="onRecordingPlayClicked.emit($event)" > diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.md b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.md new file mode 100644 index 00000000..96882601 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.md @@ -0,0 +1,9 @@ +## API Directives +With the following directives you can modify the default User Interface with the aim of fully customizing your videoconference application. + + +| **Parameter** | **Type** | **Reference** | +|:--------------------------------: | :-------: | :---------------------------------------------: | +| **recordingActivity** | `boolean` | [ActivitiesPanelRecordingActivityDirective](../directives/ActivitiesPanelRecordingActivityDirective.html) | +| **broadcastingActivity** | `boolean` | [ActivitiesPanelBroadcastingActivityDirective](../directives/ActivitiesPanelBroadcastingActivityDirective.html) | + \ No newline at end of file diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.scss b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.scss new file mode 100644 index 00000000..487c527e --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.scss @@ -0,0 +1,77 @@ +:host{ + .activities-body-container { + display: block !important; + overflow-y: auto; + overflow-x: hidden; + padding: 10px; + } + + .activity-icon { + display: inherit; + background-color: var(--ov-light-color); + border-radius: var(--ov-panel-radius); + margin: 10px 0px !important; + padding: 10px; + } + + .activity-title, .activity-subtitle { + color: var(--ov-panel-text-color); + } + + .activity-subtitle { + font-style: italic; + font-size: 11px !important; + display: block; + } + .activity-title { + font-weight: bold !important; + } + + .activity-action-buttons { + font-weight: 600; + position: absolute; + right: 15px; + top: 0px; + } + + ::ng-deep .mat-content { + display: block !important; + } + + ::ng-deep .mdc-list-item__content { + padding-left: 10px !important; + align-self: center !important; + } + + ::ng-deep .mat-expansion-panel-header { + padding: 0px 5px !important; + height: 65px !important; + } + + ::ng-deep .mat-mdc-list-base .mat-mdc-list-item .mat-list-item-content, + .mat-list-base .mat-list-option .mat-list-item-content { + padding: 0px !important; + } + + ::ng-deep mat-expansion-panel .mat-expansion-panel-body { + padding: 0px !important; + min-height: 400px; + } + ::ng-deep .mat-expansion-panel-header-description { + flex-grow: 0 !important; + } + + ::ng-deep .mat-expansion-panel { + box-shadow: none !important; + background-color: var(--ov-panel-background) !important; + } + + ::ng-deep .no-body .mat-expansion-panel-content { + display: none !important; + } + + ::ng-deep .mdc-list-item--with-leading-icon.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end::before{ + max-height: 24px; + height: 24px; + } +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/activities-panel.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.spec.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/activities-panel.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.spec.ts diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.ts new file mode 100644 index 00000000..e7a8b500 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.ts @@ -0,0 +1,138 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, OnInit, Output } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { PanelStatusInfo, PanelType } from '../../../models/panel.model'; +import { OpenViduComponentsConfigService } from '../../../services/config/openvidu-components-angular.config.service'; +import { PanelService } from '../../../services/panel/panel.service'; +import { + RecordingDeleteRequestedEvent, + RecordingDownloadClickedEvent, + RecordingPlayClickedEvent, + RecordingStartRequestedEvent, + RecordingStopRequestedEvent +} from '../../../models/recording.model'; +import { BroadcastingStartRequestedEvent, BroadcastingStopRequestedEvent } from '../../../models/broadcasting.model'; + +/** + * The **ActivitiesPanelComponent** is the component that allows showing the activities panel. + * This panel shows the recording and broadcasting activities. + */ +@Component({ + selector: 'ov-activities-panel', + templateUrl: './activities-panel.component.html', + styleUrls: ['../panel.component.scss', './activities-panel.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ActivitiesPanelComponent implements OnInit { + /** + * This event is fired when the user clicks on the start recording button. + * It provides the {@link RecordingStartRequestedEvent} payload as event data. + */ + @Output() onRecordingStartRequested: EventEmitter = new EventEmitter(); + + /** + * Provides event notifications that fire when stop recording button has been clicked. + * It provides the {@link RecordingStopRequestedEvent} payload as event data. + */ + @Output() onRecordingStopRequested: EventEmitter = new EventEmitter(); + + /** + * Provides event notifications that fire when delete recording button has been clicked. + * It provides the {@link RecordingDeleteRequestedEvent} payload as event data. + */ + @Output() onRecordingDeleteRequested: EventEmitter = new EventEmitter(); + + /** + * Provides event notifications that fire when download recording button has been clicked. + * It provides the {@link RecordingDownloadClickedEvent} payload as event data. + */ + @Output() onRecordingDownloadClicked: EventEmitter = new EventEmitter(); + + /** + * Provides event notifications that fire when play recording button has been clicked. + * It provides the {@link RecordingPlayClickedEvent} payload as event data. + */ + @Output() onRecordingPlayClicked: EventEmitter = new EventEmitter(); + + /** + * Provides event notifications that fire when start broadcasting button is clicked. + * It provides the {@link BroadcastingStartRequestedEvent} payload as event data. + */ + @Output() onBroadcastingStartRequested: EventEmitter = + new EventEmitter(); + + /** + * Provides event notifications that fire when stop broadcasting button is clicked. + * It provides the {@link BroadcastingStopRequestedEvent} payload as event data. + */ + @Output() onBroadcastingStopRequested: EventEmitter = + new EventEmitter(); + + /** + * @internal + */ + expandedPanel: string = ''; + /** + * @internal + */ + showRecordingActivity: boolean = true; + /** + * @internal + */ + showBroadcastingActivity: boolean = true; + private panelSubscription: Subscription; + private recordingActivitySub: Subscription; + private broadcastingActivitySub: Subscription; + + /** + * @internal + */ + constructor( + private panelService: PanelService, + private libService: OpenViduComponentsConfigService, + private cd: ChangeDetectorRef + ) {} + + /** + * @internal + */ + ngOnInit(): void { + this.subscribeToPanelToggling(); + this.subscribeToActivitiesPanelDirective(); + } + + /** + * @internal + */ + ngOnDestroy() { + if (this.panelSubscription) this.panelSubscription.unsubscribe(); + if (this.recordingActivitySub) this.recordingActivitySub.unsubscribe(); + if (this.broadcastingActivitySub) this.broadcastingActivitySub.unsubscribe(); + } + + /** + * @internal + */ + close() { + this.panelService.togglePanel(PanelType.ACTIVITIES); + } + + private subscribeToPanelToggling() { + this.panelSubscription = this.panelService.panelStatusObs.subscribe((ev: PanelStatusInfo) => { + if (ev.panelType === PanelType.ACTIVITIES && !!ev.subOptionType) { + this.expandedPanel = ev.subOptionType; + } + }); + } + + private subscribeToActivitiesPanelDirective() { + this.recordingActivitySub = this.libService.recordingActivity$.subscribe((value: boolean) => { + this.showRecordingActivity = value; + this.cd.markForCheck(); + }); + + this.broadcastingActivitySub = this.libService.broadcastingActivity$.subscribe((value: boolean) => { + this.showBroadcastingActivity = value; + this.cd.markForCheck(); + }); + } +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.html similarity index 94% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.html rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.html index 868c3056..d843deab 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.html +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.html @@ -1,4 +1,4 @@ - + @@ -24,7 +24,7 @@ error sensors -

{{ 'PANEL.STREAMING.TITLE' | translate }}

+

{{ 'PANEL.STREAMING.TITLE' | translate }}

{{ 'PANEL.STREAMING.SUBTITLE' | translate }}

-
+
@@ -54,14 +54,7 @@
-
+
- play_circle + play_circle
-
+

{{ 'CONTENT_SUBTITLE' | translate }}

{{ 'PANEL.STREAMING.REQUIRED_URL' | translate }}

-

{{ broadcastingError.message }}

-

+

{{ broadcastingError }}

- - -

{{ 'PANEL.STREAMING.NO_MODERATOR' | translate }}

diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.md b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.md new file mode 100644 index 00000000..f1c593f8 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.md @@ -0,0 +1,7 @@ +## API Directives +With the following directives you can modify the default User Interface with the aim of fully customizing your videoconference application. + + +_No API directives available for this component_. + + \ No newline at end of file diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.css b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.scss similarity index 97% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.css rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.scss index 2f7acf43..9d120ec1 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.css +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.scss @@ -126,6 +126,9 @@ mat-expansion-panel { padding: 10px; margin: 10px; border-radius: var(--ov-panel-radius); + order: 3; + justify-content: space-evenly; + align-items: center; } .input-container input { diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.spec.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.spec.ts diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.ts similarity index 50% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.ts index 319a28a1..bacdd57f 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/broadcasting-activity/broadcasting-activity.component.ts @@ -1,38 +1,48 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Subscription } from 'rxjs'; -import { BroadcastingError, BroadcastingStatus } from '../../../../models/broadcasting.model'; +import { + BroadcastingStartRequestedEvent, + BroadcastingStatus, + BroadcastingStatusInfo, + BroadcastingStopRequestedEvent +} from '../../../../models/broadcasting.model'; import { BroadcastingService } from '../../../../services/broadcasting/broadcasting.service'; -import { OpenViduAngularConfigService } from '../../../../services/config/openvidu-angular.config.service'; import { ParticipantService } from '../../../../services/participant/participant.service'; +import { OpenViduService } from '../../../../services/openvidu/openvidu.service'; +/** + * The **BroadcastingActivityComponent** is the component that allows showing the broadcasting activity. + * + */ @Component({ selector: 'ov-broadcasting-activity', templateUrl: './broadcasting-activity.component.html', - styleUrls: ['./broadcasting-activity.component.css', '../activities-panel.component.css'], + styleUrls: ['./broadcasting-activity.component.scss', '../activities-panel.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) + +// TODO: Allow to add more than one broadcast url +// TODO: allow to choose the layout of the broadcast export class BroadcastingActivityComponent implements OnInit { /** - * Provides event notifications that fire when start broadcasting button has been clicked. - * The broadcasting should be started using the REST API. + * Provides event notifications that fire when start broadcasting button is clicked. + * It provides the {@link BroadcastingStartRequestedEvent} payload as event data. */ - @Output() onStartBroadcastingClicked: EventEmitter = new EventEmitter(); + @Output() onBroadcastingStartRequested: EventEmitter = + new EventEmitter(); /** - * Provides event notifications that fire when stop broadcasting button has been clicked. - * The broadcasting should be stopped using the REST API. + * Provides event notifications that fire when stop broadcasting button is clicked. + * It provides the {@link BroadcastingStopRequestedEvent} payload as event data. */ - @Output() onStopBroadcastingClicked: EventEmitter = new EventEmitter(); + @Output() onBroadcastingStopRequested: EventEmitter = + new EventEmitter(); /** * @internal */ urlRequiredError: boolean = false; - /** - * @internal - */ - oldBroadcastingStatus: BroadcastingStatus; /** * @internal */ @@ -46,12 +56,16 @@ export class BroadcastingActivityComponent implements OnInit { /** * @internal */ - broadcastingError: BroadcastingError | undefined; + broadcastingError: string | undefined; /** * @internal */ broadcastingStatus: BroadcastingStatus = BroadcastingStatus.STOPPED; + /** + * @internal + */ + broadcastingId: string | undefined; /** * @internal */ @@ -59,17 +73,9 @@ export class BroadcastingActivityComponent implements OnInit { /** * @internal */ - opened: boolean = false; - /** - * @internal - */ - isSessionCreator: boolean = false; - /** - * @internal - */ - isBroadcastModuleAvailable: boolean = true; + isPanelOpened: boolean = false; + private broadcastingSub: Subscription; - private broadcastingErrorSub: Subscription; /** * @internal @@ -77,7 +83,7 @@ export class BroadcastingActivityComponent implements OnInit { constructor( private broadcastingService: BroadcastingService, private participantService: ParticipantService, - private libService: OpenViduAngularConfigService, + private openviduService: OpenViduService, private cd: ChangeDetectorRef ) {} @@ -85,9 +91,7 @@ export class BroadcastingActivityComponent implements OnInit { * @internal */ ngOnInit(): void { - this.isSessionCreator = this.participantService.amIModerator(); this.subscribeToBroadcastingStatus(); - this.subscribeToBroadcastingError(); } /** @@ -95,21 +99,13 @@ export class BroadcastingActivityComponent implements OnInit { */ ngOnDestroy() { if (this.broadcastingSub) this.broadcastingSub.unsubscribe(); - if (this.broadcastingErrorSub) this.broadcastingErrorSub.unsubscribe(); } /** * @internal */ - panelOpened() { - this.opened = true; - } - - /** - * @internal - */ - panelClosed() { - this.opened = false; + setPanelOpened(value: boolean) { + this.isPanelOpened = value; } /** @@ -128,10 +124,11 @@ export class BroadcastingActivityComponent implements OnInit { */ startBroadcasting() { if (!!this.broadcastUrl) { - this.isBroadcastModuleAvailable = true; - this.broadcastingError = undefined; - this.broadcastingService.updateStatus(BroadcastingStatus.STARTING); - this.onStartBroadcastingClicked.emit(this.broadcastUrl); + const payload: BroadcastingStartRequestedEvent = { + roomName: this.openviduService.getRoomName(), + broadcastUrl: this.broadcastUrl + }; + this.onBroadcastingStartRequested.emit(payload); } this.urlRequiredError = !this.broadcastUrl; } @@ -140,27 +137,21 @@ export class BroadcastingActivityComponent implements OnInit { * @internal */ stopBroadcasting() { - this.onStopBroadcastingClicked.emit(); - this.broadcastingService.updateStatus(BroadcastingStatus.STOPPING); + const payload: BroadcastingStopRequestedEvent = { + roomName: this.openviduService.getRoomName(), + broadcastingId: this.broadcastingId as string + }; + this.broadcastingService.setBroadcastingStopped(); + this.onBroadcastingStopRequested.emit(payload); } private subscribeToBroadcastingStatus() { - this.broadcastingSub = this.broadcastingService.broadcastingStatusObs.subscribe( - (ev: { status: BroadcastingStatus; time?: Date } | undefined) => { - if (!!ev) { - this.broadcastingStatus = ev.status; - this.cd.markForCheck(); - } - } - ); - } - - private subscribeToBroadcastingError() { - this.broadcastingErrorSub = this.libService.broadcastingErrorObs.subscribe((error: BroadcastingError | undefined) => { - if (!!error) { + this.broadcastingSub = this.broadcastingService.broadcastingStatusObs.subscribe((event: BroadcastingStatusInfo | undefined) => { + if (!!event) { + const { status, broadcastingId, error } = event; + this.broadcastingStatus = status; this.broadcastingError = error; - this.isBroadcastModuleAvailable = error.broadcastAvailable; - this.broadcastingService.updateStatus(BroadcastingStatus.FAILED); + this.broadcastingId = broadcastingId; this.cd.markForCheck(); } }); diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.html similarity index 88% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.html rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.html index cd81237c..ed55732f 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.html +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.html @@ -1,4 +1,4 @@ - + @@ -21,7 +21,7 @@ error radio_button_checked
-

{{ 'PANEL.RECORDING.TITLE' | translate }}

+

{{ 'PANEL.RECORDING.TITLE' | translate }}

{{ 'PANEL.RECORDING.SUBTITLE' | translate }}

-
+
-
+
-

{{ 'PANEL.RECORDING.CONTENT_TITLE' | translate }}

- {{ 'PANEL.RECORDING.CONTENT_SUBTITLE' | translate }} +

{{ 'PANEL.RECORDING.CONTENT_TITLE' | translate }}

+ {{ 'PANEL.RECORDING.CONTENT_SUBTITLE' | translate }} -
+
@@ -69,7 +69,7 @@ {{ 'PANEL.RECORDING.STARTING' | translate }} {{ 'PANEL.RECORDING.STOPPING' | translate }} Message: - {{ recordingError | json }} + {{ recordingError }}
- - -

{{ 'PANEL.RECORDING.NO_MODERATOR' | translate }}

- + -
+
+ - - + video_file
- {{ recording.id }} + {{ recording.filename }}
-
+
+ {{ 'PANEL.RECORDING.IN_PROGRESS' | translate }} +
+ +
{{ recording.duration | duration }} - | {{ recording.size / 1024 / 1024 | number : '1.1-2' }} MBs + | {{ recording.size / 1024 / 1024 | number: '1.1-2' }} MBs +
+
+ {{ recording.startedAt | date: 'HH:mm - dd/MM/yyyy' }}
-
{{ recording.createdAt | date : 'HH:mm - dd/MM/yyyy' }}
-

diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.css b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.scss similarity index 95% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.css rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.scss index b4d13300..6e73be89 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.css +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.scss @@ -11,6 +11,7 @@ background-color: var(--ov-light-color); width: 60px; height: 60px; + line-height: inherit; } .effect-button:hover { @@ -21,7 +22,7 @@ border: 2px solid var(--ov-tertiary-color); } -#hard-blur-btn .mat-icon { +#hard_blur-btn .mat-icon { font-weight: bold !important; } diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.spec.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.spec.ts diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.ts new file mode 100644 index 00000000..b2a696d5 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/background-effects-panel/background-effects-panel.component.ts @@ -0,0 +1,65 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { BackgroundEffect, EffectType } from '../../../models/background-effect.model'; +import { PanelType } from '../../../models/panel.model'; +import { PanelService } from '../../../services/panel/panel.service'; +import { VirtualBackgroundService } from '../../../services/virtual-background/virtual-background.service'; + +/** + * @internal + */ +@Component({ + selector: 'ov-background-effects-panel', + templateUrl: './background-effects-panel.component.html', + styleUrls: ['../panel.component.scss', './background-effects-panel.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class BackgroundEffectsPanelComponent implements OnInit { + backgroundSelectedId: string; + effectType = EffectType; + backgroundImages: BackgroundEffect[] = []; + noEffectAndBlurredBackground: BackgroundEffect[] = []; + private backgrounds: BackgroundEffect[]; + private backgroundSubs: Subscription; + + /** + * @internal + * @param panelService + * @param backgroundService + * @param cd + */ + constructor( + private panelService: PanelService, + private backgroundService: VirtualBackgroundService, + private cd: ChangeDetectorRef + ) {} + + ngOnInit(): void { + this.subscribeToBackgroundSelected(); + this.backgrounds = this.backgroundService.getBackgrounds(); + this.noEffectAndBlurredBackground = this.backgrounds.filter((f) => f.type === EffectType.BLUR || f.type === EffectType.NONE); + this.backgroundImages = this.backgrounds.filter((f) => f.type === EffectType.IMAGE); + } + + ngOnDestroy() { + if (this.backgroundSubs) this.backgroundSubs.unsubscribe(); + } + subscribeToBackgroundSelected() { + this.backgroundSubs = this.backgroundService.backgroundIdSelected$.subscribe((id) => { + this.backgroundSelectedId = id; + this.cd.markForCheck(); + }); + } + + close() { + this.panelService.togglePanel(PanelType.BACKGROUND_EFFECTS); + } + + async applyBackground(effect: BackgroundEffect) { + if (effect.type === EffectType.NONE) { + await this.backgroundService.removeBackground(); + } else { + await this.backgroundService.applyBackground(effect); + } + } +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/chat-panel/chat-panel.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/chat-panel/chat-panel.component.html similarity index 58% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/chat-panel/chat-panel.component.html rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/chat-panel/chat-panel.component.html index 23dcb76f..f186e391 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/chat-panel/chat-panel.component.html +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/chat-panel/chat-panel.component.html @@ -1,21 +1,21 @@ -
-
+
+

{{ 'PANEL.CHAT.TITLE' | translate }}

-
+

{{ 'PANEL.CHAT.SUBTITLE' | translate }}

-
+
-
+

{{ 'PANEL.CHAT.YOU' | translate }}

-

{{ data.nickname }}

+

{{ data.participantName }}

@@ -24,7 +24,7 @@
-
+
-
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/chat-panel/chat-panel.component.md b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/chat-panel/chat-panel.component.md new file mode 100644 index 00000000..876bc778 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/chat-panel/chat-panel.component.md @@ -0,0 +1,19 @@ + + + +## Structural Directives + +| **Directive** | **Reference** | +| :---------------: | :-------------------------------------------------------: | +| **\*ovChatPanel** | [ChatPanelDirective](../directives/ChatPanelDirective.html) | + + + +## API Directives + +With the following directives you can modify the default User Interface with the aim of fully customizing your videoconference application. + + +_No API directives available for this component_. + + diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/chat-panel/chat-panel.component.css b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/chat-panel/chat-panel.component.scss similarity index 69% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/chat-panel/chat-panel.component.css rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/chat-panel/chat-panel.component.scss index db507860..e45a0f11 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/chat-panel/chat-panel.component.css +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/chat-panel/chat-panel.component.scss @@ -1,36 +1,46 @@ .text-container { - /* padding: 5px; */ background-color: var(--ov-light-color); color: var(--ov-panel-text-color); text-align: center; - /* margin: 5px 5px; */ font-size: 12px; + flex: inherit; } .text-info { - margin: 3px; + margin: auto; +} + +.vertical-align { + line-height: 59px; } .messages-container { - display: block !important; overflow-y: auto; overflow-x: hidden; padding: 10px; + flex: 75%; + display: block; + justify-content: space-evenly; + align-items: none; } .input-container { - height: 25px; + height: 65px; display: flex; background-color: var(--ov-light-color); - padding: 10px; + padding: 10px 5px 10px 10px; margin: 10px; border-radius: var(--ov-panel-radius); + order: 3; + justify-content: space-evenly; + align-items: none; } .input-container textarea { width: 100%; - height: 16px; + height: 60px; margin: auto; + margin-right: 3px; background-color: transparent; display: block; border: none; @@ -39,7 +49,7 @@ white-space: pre-wrap; resize: none; outline: none; - + font-size: 16px; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; @@ -61,7 +71,7 @@ font-size: 14px; } -.nickname-container p { +.participant-name-container p { font-size: 13px; font-weight: bold; color: var(--ov-panel-text-color); @@ -79,12 +89,16 @@ } #send-btn { - border-radius: var(--ov-buttons-radius); + border-radius: var(--ov-panel-radius); + color: var(--ov-light-color); + background-color: var(--ov-tertiary-color); + align-self: center; + height: 75px; } /* Start message from other user */ -.message.left .msg-detail .nickname-container { +.message.left .msg-detail .participant-name-container { text-align: left; } @@ -96,7 +110,7 @@ /* Start my messages */ -.message.right .msg-detail .nickname-container { +.message.right .msg-detail .participant-name-container { text-align: right; } @@ -105,5 +119,5 @@ } ::ng-deep a:-webkit-any-link { - color: #1a73e8; + color: var(--ov-tertiary-color); } diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/chat-panel/chat-panel.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/chat-panel/chat-panel.component.spec.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/chat-panel/chat-panel.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/chat-panel/chat-panel.component.spec.ts diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/chat-panel/chat-panel.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/chat-panel/chat-panel.component.ts similarity index 58% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/chat-panel/chat-panel.component.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/chat-panel/chat-panel.component.ts index 68c10b70..15b853e4 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/chat-panel/chat-panel.component.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/chat-panel/chat-panel.component.ts @@ -1,13 +1,4 @@ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - HostListener, - OnInit, - ViewChild -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { Subscription } from 'rxjs'; import { ChatMessage } from '../../../models/chat.model'; import { PanelType } from '../../../models/panel.model'; @@ -16,32 +7,12 @@ import { PanelService } from '../../../services/panel/panel.service'; /** * - * The **ChatPanelComponent** is hosted inside of the {@link PanelComponent}. - * It is in charge of displaying the session chat. - * - *
- - *
- * - *

OpenVidu Angular Directives

- * - * The ChatPanelComponent can be replaced with a custom component. It provides us the following {@link https://angular.io/guide/structural-directives Angular structural directives} - * for doing this. - * - * | **Directive** | **Reference** | - * |:----------------------------------:|:---------------------------------------------:| - * | ***ovChatPanel** | {@link ChatPanelDirective} | - * - * - *
- *
+ * The **ChatPanelComponent** is an integral part of the {@link PanelComponent} and serves as the interface for displaying the session chat. */ @Component({ selector: 'ov-chat-panel', templateUrl: './chat-panel.component.html', - styleUrls: ['../panel.component.css','./chat-panel.component.css'], + styleUrls: ['../panel.component.scss', './chat-panel.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) export class ChatPanelComponent implements OnInit, AfterViewInit { @@ -57,7 +28,9 @@ export class ChatPanelComponent implements OnInit, AfterViewInit { * @ignore */ message: string; - + /** + * @ignore + */ messageList: ChatMessage[] = []; private chatMessageSubscription: Subscription; @@ -65,12 +38,22 @@ export class ChatPanelComponent implements OnInit, AfterViewInit { /** * @ignore */ - constructor(private chatService: ChatService, private panelService: PanelService, private cd: ChangeDetectorRef) {} + constructor( + private chatService: ChatService, + private panelService: PanelService, + private cd: ChangeDetectorRef + ) {} + /** + * @ignore + */ ngOnInit() { this.subscribeToMessages(); } + /** + * @ignore + */ ngAfterViewInit() { setTimeout(() => { this.scrollToBottom(); @@ -78,6 +61,9 @@ export class ChatPanelComponent implements OnInit, AfterViewInit { }, 100); } + /** + * @ignore + */ ngOnDestroy(): void { if (this.chatMessageSubscription) this.chatMessageSubscription.unsubscribe(); } @@ -93,13 +79,19 @@ export class ChatPanelComponent implements OnInit, AfterViewInit { } } - sendMessage(): void { + /** + * @ignore + */ + async sendMessage(): Promise { if (!!this.message) { - this.chatService.sendMessage(this.message); + await this.chatService.sendMessage(this.message); this.message = ''; } } + /** + * @ignore + */ scrollToBottom(): void { setTimeout(() => { try { @@ -108,6 +100,9 @@ export class ChatPanelComponent implements OnInit, AfterViewInit { }, 20); } + /** + * @ignore + */ close() { this.panelService.togglePanel(PanelType.CHAT); } diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/panel.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/panel.component.html similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/panel.component.html rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/panel.component.html diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/panel.component.md b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/panel.component.md new file mode 100644 index 00000000..22652d34 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/panel.component.md @@ -0,0 +1,30 @@ + + + +## Structural Directives + +With these kind of directives you can replace the default **PanelComponent** with your own component. You can also add your own components to the default ones. + +| **Directive** | **Reference** | +| :-----------: | :-----------------------------------------------: | +| **\*ovPanel** | [PanelDirective](../directives/PanelDirective.html) | + +It is also providing us a way to **replace the children panels** to the default panel. +It will recognise the following directive in a child element. + +| **Directive** | **Reference** | +| :-----------------------: | :-----------------------------------------------------------------------: | +| **\*ovChatPanel** | [ChatPanelDirective](../directives/ChatPanelDirective.html) | +| **\*ovParticipantsPanel** | [ParticipantsPanelDirective](../directives/ParticipantsPanelDirective.html) | +| **\*ovAdditionalPanels** | [AdditionalPanelsDirective](../directives/AdditionalPanelsDirective.html) | + + + +## API Directives + +With the following directives you can modify the default User Interface with the aim of fully customizing your videoconference application. + + +_No API directives available for this component_. + + diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/panel.component.css b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/panel.component.scss similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/panel.component.css rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/panel.component.scss diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/panel.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/panel.component.ts similarity index 52% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/panel.component.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/panel.component.ts index 5e185c2f..b458035a 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/panel.component.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/panel.component.ts @@ -1,55 +1,42 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, OnInit, TemplateRef } from '@angular/core'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ContentChild, + EventEmitter, + OnInit, + Output, + TemplateRef +} from '@angular/core'; import { skip, Subscription } from 'rxjs'; import { ActivitiesPanelDirective, AdditionalPanelsDirective, ChatPanelDirective, ParticipantsPanelDirective -} from '../../directives/template/openvidu-angular.directive'; -import { PanelEvent, PanelType } from '../../models/panel.model'; +} from '../../directives/template/openvidu-components-angular.directive'; +import { + ActivitiesPanelStatusEvent, + ChatPanelStatusEvent, + PanelStatusInfo, + PanelType, + ParticipantsPanelStatusEvent, + SettingsPanelStatusEvent +} from '../../models/panel.model'; import { PanelService } from '../../services/panel/panel.service'; +import { BackgroundEffect } from '../../models/background-effect.model'; /** * * The **PanelComponent** is hosted inside of the {@link VideoconferenceComponent}. * It is in charge of displaying the videoconference panels providing functionalities to the videoconference app - * such as the chat ({@link ChatPanelComponent}) and list of participants ({@link ParticipantsPanelComponent}) . - * - *
- - *
- * - *

OpenVidu Angular Directives

- * - * The PanelComponent can be replaced with a custom component. It provides us the following {@link https://angular.io/guide/structural-directives Angular structural directives} - * for doing this. - * - * | **Directive** | **Reference** | - * |:----------------------------------:|:---------------------------------------------:| - * | ***ovPanel** | {@link PanelDirective} | - * - *
- * - * It is also providing us a way to **replace the children panels** to the default panel. - * It will recognise the following directive in a child element. - * - * | **Directive** | **Reference** | - * |:----------------------------------:|:---------------------------------------------:| - * | ***ovChatPanel** | {@link ChatPanelDirective} | - * | ***ovParticipantsPanel** | {@link ParticipantsPanelDirective} | - * | ***ovAdditionalPanels** | {@link AdditionalPanelsDirective} | - * - * - *
- *
+ * such as the chat ({@link ChatPanelComponent}) and list of participants ({@link ParticipantsPanelComponent}) */ @Component({ selector: 'ov-panel', templateUrl: './panel.component.html', - styleUrls: ['./panel.component.css'], + styleUrls: ['./panel.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) export class PanelComponent implements OnInit { @@ -82,6 +69,9 @@ export class PanelComponent implements OnInit { */ @ContentChild('additionalPanels', { read: TemplateRef }) additionalPanelsTemplate: TemplateRef; + /** + * @ignore + */ @ContentChild(ParticipantsPanelDirective) set externalParticipantPanel(externalParticipantsPanel: ParticipantsPanelDirective) { // This directive will has value only when PARTICIPANTS PANEL component tagged with '*ovParticipantsPanel' @@ -111,6 +101,9 @@ export class PanelComponent implements OnInit { // } // } + /** + * @ignore + */ @ContentChild(ActivitiesPanelDirective) set externalActivitiesPanel(externalActivitiesPanel: ActivitiesPanelDirective) { // This directive will has value only when ACTIVITIES PANEL component tagged with '*ovActivitiesPanel' @@ -120,6 +113,9 @@ export class PanelComponent implements OnInit { } } + /** + * @ignore + */ @ContentChild(ChatPanelDirective) set externalChatPanel(externalChatPanel: ChatPanelDirective) { // This directive will has value only when CHAT PANEL component tagged with '*ovChatPanel' @@ -129,6 +125,9 @@ export class PanelComponent implements OnInit { } } + /** + * @ignore + */ @ContentChild(AdditionalPanelsDirective) set externalAdditionalPanels(externalAdditionalPanels: AdditionalPanelsDirective) { // This directive will has value only when ADDITIONAL PANELS component tagged with '*ovPanelAdditionalPanels' @@ -138,10 +137,57 @@ export class PanelComponent implements OnInit { } } + /** + * This event is fired when the chat panel status has been changed. + * It provides the new status of the chat panel represented by the {@link ChatPanelStatusEvent} object. + */ + @Output() onChatPanelStatusChanged: EventEmitter = new EventEmitter(); + + /** + * This event is fired when the participants panel status has been changed. + * It provides the new status of the participants panel represented by the {@link ParticipantsPanelStatusEvent} object. + */ + @Output() onParticipantsPanelStatusChanged: EventEmitter = + new EventEmitter(); + + /** + * This event is fired when the settings panel status has been changed. + * It provides the new status of the settings panel represented by the {@link SettingsPanelStatusEvent} object. + */ + @Output() onSettingsPanelStatusChanged: EventEmitter = new EventEmitter(); + + /** + * This event is fired when the activities panel status has been changed. + * It provides the new status of the activities panel represented by the {@link ActivitiesPanelStatusEvent} object. + */ + @Output() onActivitiesPanelStatusChanged: EventEmitter = new EventEmitter(); + + /** + * This event is fired when the background effects panel status has been changed. + * It provides the new status of the background effects panel represented by the {@link BackgroundEffectsPanelStatusEvent} object. + * @internal + */ + // @Output() onBackgroundEffectsPanelStatusChanged: EventEmitter = new EventEmitter(); + + /** + * @ignore + */ isParticipantsPanelOpened: boolean; + /** + * @ignore + */ isChatPanelOpened: boolean; + /** + * @ignore + */ isBackgroundEffectsPanelOpened: boolean; + /** + * @ignore + */ isSettingsPanelOpened: boolean; + /** + * @ignore + */ isActivitiesPanelOpened: boolean; /** @@ -150,15 +196,32 @@ export class PanelComponent implements OnInit { isExternalPanelOpened: boolean; private panelSubscription: Subscription; + private panelEmitersHandler: Map< + PanelType, + EventEmitter + > = new Map(); + /** * @ignore */ - constructor(protected panelService: PanelService, private cd: ChangeDetectorRef) {} + constructor( + private panelService: PanelService, + private cd: ChangeDetectorRef + ) {} + /** + * @ignore + */ ngOnInit(): void { this.subscribeToPanelToggling(); + this.panelEmitersHandler.set(PanelType.CHAT, this.onChatPanelStatusChanged); + this.panelEmitersHandler.set(PanelType.PARTICIPANTS, this.onParticipantsPanelStatusChanged); + this.panelEmitersHandler.set(PanelType.SETTINGS, this.onSettingsPanelStatusChanged); + this.panelEmitersHandler.set(PanelType.ACTIVITIES, this.onActivitiesPanelStatusChanged); } - + /** + * @ignore + */ ngOnDestroy() { this.isChatPanelOpened = false; this.isParticipantsPanelOpened = false; @@ -166,20 +229,38 @@ export class PanelComponent implements OnInit { } private subscribeToPanelToggling() { - this.panelSubscription = this.panelService.panelOpenedObs.pipe(skip(1)).subscribe((ev: PanelEvent) => { - this.isChatPanelOpened = ev.opened && ev.type === PanelType.CHAT; - this.isParticipantsPanelOpened = ev.opened && ev.type === PanelType.PARTICIPANTS; - this.isBackgroundEffectsPanelOpened = ev.opened && ev.type === PanelType.BACKGROUND_EFFECTS; - this.isSettingsPanelOpened = ev.opened && ev.type === PanelType.SETTINGS; - this.isActivitiesPanelOpened = ev.opened && ev.type === PanelType.ACTIVITIES; + this.panelSubscription = this.panelService.panelStatusObs.pipe(skip(1)).subscribe((ev: PanelStatusInfo) => { + this.isChatPanelOpened = ev.isOpened && ev.panelType === PanelType.CHAT; + this.isParticipantsPanelOpened = ev.isOpened && ev.panelType === PanelType.PARTICIPANTS; + this.isBackgroundEffectsPanelOpened = ev.isOpened && ev.panelType === PanelType.BACKGROUND_EFFECTS; + this.isSettingsPanelOpened = ev.isOpened && ev.panelType === PanelType.SETTINGS; + this.isActivitiesPanelOpened = ev.isOpened && ev.panelType === PanelType.ACTIVITIES; this.isExternalPanelOpened = - ev.opened && + ev.isOpened && + !this.isSettingsPanelOpened && + !this.isBackgroundEffectsPanelOpened && !this.isChatPanelOpened && !this.isParticipantsPanelOpened && - !this.isBackgroundEffectsPanelOpened && - !this.isSettingsPanelOpened && !this.isActivitiesPanelOpened; this.cd.markForCheck(); + + this.sendPanelStatusChangedEvent(ev); }); } + + private sendPanelStatusChangedEvent(event: PanelStatusInfo) { + const { panelType, isOpened, previousPanelType } = event; + + // Emit to the current panel + if (panelType) { + const panelMatch = this.panelEmitersHandler.get(panelType as PanelType); + if (panelMatch) panelMatch.emit({ isOpened }); + } + + // Emit to the previous panel if it's different from the current one + if (previousPanelType && panelType !== previousPanelType) { + const previousPanelMatch = this.panelEmitersHandler.get(previousPanelType as PanelType); + if (previousPanelMatch) previousPanelMatch.emit({ isOpened: false }); + } + } } diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.html similarity index 77% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.html rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.html index b259d056..22d33091 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.html +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/participants-panel/participant-panel-item/participant-panel-item.component.html @@ -3,19 +3,22 @@
person
-

- {{ _participant.nickname }} +

{{ _participant.name }} ({{ 'PANEL.PARTICIPANTS.YOU' | translate }})

-

{{ _participant | streamTypesEnabled }}

+

{{ _participant | tracksPublishedTypes }}

+
- + {{ 'PREJOIN.NICKNAME' | translate }} + - language + translate
{{ 'PANEL.SETTINGS.LANGUAGE' | translate }}
- +
- - - + + +
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/settings-panel/settings-panel.component.scss b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/settings-panel/settings-panel.component.scss new file mode 100644 index 00000000..77e6b11a --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/settings-panel/settings-panel.component.scss @@ -0,0 +1,53 @@ +#settings-container { + display: flex !important; + .settings-container { + display: flex; + padding: 10px; + flex: 1; + width: auto; + justify-content: space-between; + align-items: stretch; + } + + .item-menu { + padding-right: 5px; + border-right: 1px solid var(--ov-secondary-color); + width: 170px; + } + .item-menu.mobile { + width: 50px !important; + } + + .item-content { + padding: 16px; + flex-grow: 1; + width: min-content; + } + + .lang-container button { + width: 100%; + } + + mat-list-option[aria-selected='true'] { + background: var(--ov-tertiary-color) !important; + border-radius: var(--ov-panel-radius); + ::ng-deep .mat-mdc-list-item-unscoped-content, + mat-icon { + color: var(--ov-panel-background) !important; + } + } + + mat-list-option[aria-selected='false'] { + mat-icon { + color: var(--ov-panel-text-color) !important; + } + } + + ::ng-deep .mdc-list-item--with-leading-icon .mdc-list-item__start { + margin-right: 15px !important; + } + + .mat-mdc-list-base { + --mdc-list-list-item-focus-state-layer-color: transparent !important; + } +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/settings-panel/settings-panel.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/settings-panel/settings-panel.component.spec.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/settings-panel/settings-panel.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/settings-panel/settings-panel.component.spec.ts diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/settings-panel/settings-panel.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/settings-panel/settings-panel.component.ts similarity index 51% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/settings-panel/settings-panel.component.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/settings-panel/settings-panel.component.ts index 18b1ffe4..3f863711 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/settings-panel/settings-panel.component.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/settings-panel/settings-panel.component.ts @@ -1,9 +1,11 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, EventEmitter, OnInit, Output } from '@angular/core'; import { Subscription } from 'rxjs'; -import { PanelEvent, PanelSettingsOptions, PanelType } from '../../../models/panel.model'; -import { OpenViduAngularConfigService } from '../../../services/config/openvidu-angular.config.service'; +import { PanelStatusInfo, PanelSettingsOptions, PanelType } from '../../../models/panel.model'; +import { OpenViduComponentsConfigService } from '../../../services/config/openvidu-components-angular.config.service'; import { PanelService } from '../../../services/panel/panel.service'; import { PlatformService } from '../../../services/platform/platform.service'; +import { CustomDevice } from '../../../models/device.model'; +import { LangOption } from '../../../models/lang.model'; /** * @internal @@ -11,9 +13,14 @@ import { PlatformService } from '../../../services/platform/platform.service'; @Component({ selector: 'ov-settings-panel', templateUrl: './settings-panel.component.html', - styleUrls: ['../panel.component.css', './settings-panel.component.css'] + styleUrls: ['../panel.component.scss', './settings-panel.component.scss'] }) export class SettingsPanelComponent implements OnInit { + @Output() onVideoEnabledChanged = new EventEmitter(); + @Output() onVideoDeviceChanged = new EventEmitter(); + @Output() onAudioEnabledChanged = new EventEmitter(); + @Output() onAudioDeviceChanged = new EventEmitter(); + @Output() onLangChanged = new EventEmitter(); settingsOptions: typeof PanelSettingsOptions = PanelSettingsOptions; selectedOption: PanelSettingsOptions = PanelSettingsOptions.GENERAL; showCaptions: boolean = true; @@ -23,7 +30,7 @@ export class SettingsPanelComponent implements OnInit { constructor( private panelService: PanelService, private platformService: PlatformService, - private libService: OpenViduAngularConfigService + private libService: OpenViduComponentsConfigService ) {} ngOnInit() { this.isMobile = this.platformService.isMobile(); @@ -43,15 +50,15 @@ export class SettingsPanelComponent implements OnInit { } private subscribeToDirectives() { - this.captionsSubs = this.libService.captionsButtonObs.subscribe((value: boolean) => { + this.captionsSubs = this.libService.captionsButton$.subscribe((value: boolean) => { this.showCaptions = value; }); } private subscribeToPanelToggling() { - this.panelSubscription = this.panelService.panelOpenedObs.subscribe((ev: PanelEvent) => { - if (ev.type === PanelType.SETTINGS && !!ev.expand) { - this.selectedOption = ev.expand as PanelSettingsOptions; + this.panelSubscription = this.panelService.panelStatusObs.subscribe((ev: PanelStatusInfo) => { + if (ev.panelType === PanelType.SETTINGS && !!ev.subOptionType) { + this.selectedOption = ev.subOptionType as PanelSettingsOptions; } }); } diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.html new file mode 100644 index 00000000..f21b2e46 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.html @@ -0,0 +1,65 @@ +
+ +
+ + {{ 'PREJOIN.PREPARING' | translate }} +
+ +
+ + + + +
+
+
+ +
+
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ {{ _error }} +
+ +
+ +
+
+
+
+
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.scss b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.scss new file mode 100644 index 00000000..aec74c60 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.scss @@ -0,0 +1,199 @@ +:host { + --ov-primary-color-lighter: color-mix(in srgb, var(--ov-primary-color), #fff 15%); + + #loading-container { + position: absolute; + top: 40%; + bottom: 0; + left: 0; + right: 0; + margin: auto; + text-align: -webkit-center; + text-align: -moz-center; + color: #fff; + } + + .container { + height: 100%; + background-color: var(--ov-secondary-color); + display: flex; + } + + #features-container { + margin: auto; + width: 70vh; + height: 70vh; + margin-right: 0; + box-shadow: -6px 2px 20px 0px #0003; + background-color: var(--ov-accent-color); + } + + #prejoin-frame { + display: grid; + align-content: center; + margin: auto; + // margin-left: 0px; + border-radius: var(--ov-panel-radius); + width: 70vh; + height: 85vh; + padding: 20px; + background-color: var(--ov-panel-background); + box-shadow: 6px 4px 20px 0px #0003; + position: relative; + } + + ::ng-deep .lang-btn { + position: absolute; + top: 10px; + right: 10px; + height: 25px !important; + font-size: 14px !important; + } + + #branding-logo { + position: absolute; + top: 10px; + left: 10px; + max-width: 50px; + } + + #branding-logo { + border-radius: var(--ov-panel-radius); + max-width: 35px; + max-height: 35px; + margin-right: 10px; + } + + .video-container { + margin: auto; + min-height: 45vh; + max-height: 45vh; + height: 45vh; + max-width: 80%; + } + #video-poster { + height: 100%; + width: 100%; + position: relative; + } + + .media-panel { + background-color: var(--ov-light-color); + } + .media-controls-container { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + max-width: 80%; + margin: auto; + height: 25vh; + } + + .participant-name-container { + display: block !important; + width: 100%; + margin-bottom: 2%; + } + + .video-controls-container, + .audio-controls-container { + width: calc(50% - 3px); + margin-top: 10px; + margin-bottom: 10px; + } + + .join-btn-container { + width: 100%; + } + + #join-button { + background-color: var(--ov-tertiary-color); + color: var(--ov-text-color); + font-weight: bold; + border-radius: var(--ov-video-radius); + width: 100%; + height: 50px; + } + + .error { + font-size: 12px; + font-weight: bold; + font-style: italic; + color: var(--ov-warn-color); + } + + /* Styles for screens up to 768px wide */ + @media (max-width: 768px) { + /* Specific styles for small screens */ + .container { + padding: 0px; + } + #prejoin-frame { + margin: auto; + height: 100%; + padding: 0px; + } + .video-container { + height: 50vh; + width: 90%; + max-width: 90%; + } + .media-controls-container { + height: 30vh; + width: 90%; + max-width: 90%; + } + } + + /* Styles for screens from 768px to 992px wide */ + @media (min-width: 768px) and (max-width: 992px) { + /* Specific styles for medium screens */ + } + + /* Styles for screens from 992px to 1200px wide */ + @media (min-width: 992px) and (max-width: 1200px) { + /* Specific styles for large screens */ + } + + /* Styles for screens over 1200px wide */ + @media (min-width: 1200px) { + /* Specific styles for extra-large screens */ + } + + /* Styles for screens with vertical orientation */ + @media (orientation: portrait) { + /* Specific styles for screens in portrait orientation */ + } + + /* Styles for screens with horizontal orientation */ + @media (max-width: 800) and (orientation: landscape) { + /* Specific styles for screens in landscape orientation */ + .container { + height: 100%; + padding: 10px 60px; + } + .prejoin-toolbar { + display: none; + } + + .video-controls-container, + .audio-controls-container { + width: 48%; + margin-bottom: 2%; + } + } + + /* Styles for screens with maximum height of 630px */ + @media (max-height: 630px) { + .video-container { + max-width: 85%; + height: 37vh; + min-height: 37vh; + } + + .media-controls-container { + height: 35vh; + max-width: 85%; + } + } +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/pre-join/pre-join.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.spec.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/pre-join/pre-join.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.spec.ts diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.ts new file mode 100644 index 00000000..d9fb1d0a --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.ts @@ -0,0 +1,167 @@ +import { ChangeDetectorRef, Component, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { ILogger } from '../../models/logger.model'; +import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.service'; +import { OpenViduComponentsConfigService } from '../../services/config/openvidu-components-angular.config.service'; +import { LayoutService } from '../../services/layout/layout.service'; +import { LoggerService } from '../../services/logger/logger.service'; +import { OpenViduService } from '../../services/openvidu/openvidu.service'; +import { TranslateService } from '../../services/translate/translate.service'; +import { LocalTrack } from 'livekit-client'; +import { CustomDevice } from '../../models/device.model'; +import { LangOption } from '../../models/lang.model'; +import { StorageService } from '../../services/storage/storage.service'; + +/** + * @internal + */ +@Component({ + selector: 'ov-pre-join', + templateUrl: './pre-join.component.html', + styleUrls: ['./pre-join.component.scss'] +}) +export class PreJoinComponent implements OnInit, OnDestroy { + @Input() set error(error: { name: string; message: string } | undefined) { + if (error) this._error = error.message ?? error.name; + } + @Output() onVideoDeviceChanged = new EventEmitter(); + @Output() onAudioDeviceChanged = new EventEmitter(); + @Output() onVideoEnabledChanged = new EventEmitter(); + @Output() onAudioEnabledChanged = new EventEmitter(); + @Output() onLangChanged = new EventEmitter(); + @Output() onReadyToJoin = new EventEmitter(); + + _error: string | undefined; + + windowSize: number; + isLoading = true; + participantName: string | undefined; + + /** + * @ignore + */ + isMinimal: boolean = false; + showLogo: boolean = true; + + videoTrack: LocalTrack | undefined; + audioTrack: LocalTrack | undefined; + private tracks: LocalTrack[]; + private log: ILogger; + private screenShareStateSubscription: Subscription; + private minimalSub: Subscription; + private displayLogoSub: Subscription; + private shouldRemoveTracksWhenComponentIsDestroyed: boolean = true; + + @HostListener('window:resize') + sizeChange() { + this.windowSize = window.innerWidth; + this.layoutService.update(); + } + + constructor( + private layoutService: LayoutService, + private loggerSrv: LoggerService, + private libService: OpenViduComponentsConfigService, + private cdkSrv: CdkOverlayService, + private openviduService: OpenViduService, + private storageService: StorageService, + private translateService: TranslateService, + private changeDetector: ChangeDetectorRef + ) { + this.log = this.loggerSrv.get('PreJoinComponent'); + } + + async ngOnInit() { + this.subscribeToPrejoinDirectives(); + try { + const cameraEnabled = this.storageService.isCameraEnabled(); + const microphoneEnabled = this.storageService.isMicrophoneEnabled(); + this.tracks = await this.openviduService.createLocalTracks(cameraEnabled, microphoneEnabled); + this.openviduService.setLocalTracks(this.tracks); + this.videoTrack = this.tracks.find((track) => track.kind === 'video'); + this.audioTrack = this.tracks.find((track) => track.kind === 'audio'); + } catch (error) { + this.log.e('Error creating local tracks:', error); + } + + this.windowSize = window.innerWidth; + this.isLoading = false; + } + + ngAfterContentChecked(): void { + this.changeDetector.detectChanges(); + } + + async ngOnDestroy() { + this.cdkSrv.setSelector('body'); + if (this.screenShareStateSubscription) this.screenShareStateSubscription.unsubscribe(); + if (this.minimalSub) this.minimalSub.unsubscribe(); + if (this.displayLogoSub) this.displayLogoSub.unsubscribe(); + + if (this.shouldRemoveTracksWhenComponentIsDestroyed) { + this.tracks.forEach((track) => { + track.stop(); + }); + } + } + + onDeviceSelectorClicked() { + // Some devices as iPhone do not show the menu panels correctly + // Updating the container where the panel is added fix the problem. + this.cdkSrv.setSelector('#prejoin-container'); + } + + joinSession() { + if (!this.participantName) { + this._error = this.translateService.translate('PREJOIN.NICKNAME_REQUIRED'); + return; + } + + // Mark tracks as permanent for avoiding to be removed in ngOnDestroy + this.shouldRemoveTracksWhenComponentIsDestroyed = false; + this.onReadyToJoin.emit(); + } + + onParticipantNameChanged(name: string) { + this.participantName = name; + } + + onEnterPressed() { + this.joinSession(); + } + + private subscribeToPrejoinDirectives() { + this.minimalSub = this.libService.minimal$.subscribe((value: boolean) => { + this.isMinimal = value; + // this.cd.markForCheck(); + }); + this.displayLogoSub = this.libService.displayLogo$.subscribe((value: boolean) => { + this.showLogo = value; + // this.cd.markForCheck(); + }); + this.libService.participantName$.subscribe((value: string) => { + if (value) this.participantName = value; + // this.cd.markForCheck(); + }); + } + + async videoEnabledChanged(enabled: boolean) { + if (enabled && !this.videoTrack) { + const newVideoTrack = await this.openviduService.createLocalTracks(true, false); + this.videoTrack = newVideoTrack[0]; + this.tracks.push(this.videoTrack); + this.openviduService.setLocalTracks(this.tracks); + } + this.onVideoEnabledChanged.emit(enabled); + } + + async audioEnabledChanged(enabled: boolean) { + if (enabled && !this.audioTrack) { + const newAudioTrack = await this.openviduService.createLocalTracks(false, true); + this.audioTrack = newAudioTrack[0]; + this.tracks.push(this.audioTrack); + this.openviduService.setLocalTracks(this.tracks); + } + this.onAudioEnabledChanged.emit(enabled); + } +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/session/session.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.html similarity index 77% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/session/session.component.html rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.html index 8acb90ca..b891ec5c 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/session/session.component.html +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.html @@ -1,9 +1,9 @@ -
+
- {{ 'PREJOIN.PREPARING' | translate }} + {{ 'ROOM.JOINING' | translate }}
-
+
-
+
diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/session/session.component.css b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.scss similarity index 94% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/session/session.component.css rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.scss index 53c6aaa9..005964f0 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/session/session.component.css +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.scss @@ -1,20 +1,19 @@ -#spinner { - position: absolute; - top: 40%; - bottom: 0; - left: 0; - right: 0; - margin: auto; - text-align: -webkit-center; - text-align: -moz-center; - color: var(--ov-panel-background); -} - #session-container { background-color: var(--ov-primary-color); /* min-width: 400px; */ height: 100%; } +#spinner { + position: absolute; + top: 40%; + bottom: 0; + left: 0; + right: 0; + margin: auto; + text-align: -webkit-center; + text-align: -moz-center; + color: var(--ov-text-color); +} .sidenav-container { position: relative; @@ -122,4 +121,4 @@ ::ng-deep .mat-drawer { background-color: transparent !important; -} \ No newline at end of file +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/session/session.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.spec.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/session/session.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.spec.ts diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.ts new file mode 100644 index 00000000..d0dae279 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.ts @@ -0,0 +1,484 @@ +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ContentChild, + ElementRef, + EventEmitter, + HostListener, + OnDestroy, + OnInit, + Output, + TemplateRef, + ViewChild +} from '@angular/core'; + +import { ILogger } from '../../models/logger.model'; +import { animate, style, transition, trigger } from '@angular/animations'; +import { MatDrawerContainer, MatSidenav } from '@angular/material/sidenav'; +import { skip, Subscription } from 'rxjs'; +import { SidenavMode } from '../../models/layout.model'; +import { PanelStatusInfo, PanelType } from '../../models/panel.model'; +import { DataTopic } from '../../models/data-topic.model'; +import { RoomStatusData } from '../../models/room.model'; +import { ActionService } from '../../services/action/action.service'; +import { BroadcastingService } from '../../services/broadcasting/broadcasting.service'; +import { CaptionService } from '../../services/caption/caption.service'; +import { ChatService } from '../../services/chat/chat.service'; +import { OpenViduComponentsConfigService } from '../../services/config/openvidu-components-angular.config.service'; +import { LayoutService } from '../../services/layout/layout.service'; +import { LoggerService } from '../../services/logger/logger.service'; +import { OpenViduService } from '../../services/openvidu/openvidu.service'; +import { PanelService } from '../../services/panel/panel.service'; +import { ParticipantService } from '../../services/participant/participant.service'; +import { PlatformService } from '../../services/platform/platform.service'; +import { RecordingService } from '../../services/recording/recording.service'; +import { TranslateService } from '../../services/translate/translate.service'; +import { VirtualBackgroundService } from '../../services/virtual-background/virtual-background.service'; +import { + DataPacket_Kind, + DisconnectReason, + LocalParticipant, + Participant, + RemoteParticipant, + RemoteTrack, + RemoteTrackPublication, + Room, + RoomEvent, + Track +} from 'livekit-client'; +import { ParticipantModel } from '../../models/participant.model'; + +/** + * @internal + */ + +@Component({ + selector: 'ov-session', + templateUrl: './session.component.html', + styleUrls: ['./session.component.scss'], + animations: [trigger('sessionAnimation', [transition(':enter', [style({ opacity: 0 }), animate('50ms', style({ opacity: 1 }))])])], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class SessionComponent implements OnInit, OnDestroy { + @ContentChild('toolbar', { read: TemplateRef }) toolbarTemplate: TemplateRef; + @ContentChild('panel', { read: TemplateRef }) panelTemplate: TemplateRef; + @ContentChild('layout', { read: TemplateRef }) layoutTemplate: TemplateRef; + /** + * Provides event notifications that fire when OpenVidu Room is created. + * + */ + @Output() onRoomCreated: EventEmitter = new EventEmitter(); + + /** + * Provides event notifications that fire when local participant is created. + */ + @Output() onParticipantCreated: EventEmitter = new EventEmitter(); + + room: Room; + sideMenu: MatSidenav; + sidenavMode: SidenavMode = SidenavMode.SIDE; + settingsPanelOpened: boolean; + drawer: MatDrawerContainer; + loading: boolean = true; + + private shouldDisconnectRoomWhenComponentIsDestroyed: boolean = true; + private readonly SIDENAV_WIDTH_LIMIT_MODE = 790; + private menuSubscription: Subscription; + private layoutWidthSubscription: Subscription; + private updateLayoutInterval: NodeJS.Timeout; + private captionLanguageSubscription: Subscription; + private log: ILogger; + + constructor( + private actionService: ActionService, + private openviduService: OpenViduService, + private participantService: ParticipantService, + private loggerSrv: LoggerService, + private chatService: ChatService, + private libService: OpenViduComponentsConfigService, + private layoutService: LayoutService, + private panelService: PanelService, + private recordingService: RecordingService, + private broadcastingService: BroadcastingService, + private translateService: TranslateService, + private captionService: CaptionService, + private platformService: PlatformService, + private backgroundService: VirtualBackgroundService, + private cd: ChangeDetectorRef + ) { + this.log = this.loggerSrv.get('SessionComponent'); + } + + @HostListener('window:beforeunload') + beforeunloadHandler() { + this.disconnectRoom(); + } + + @HostListener('window:resize') + sizeChange() { + this.layoutService.update(); + } + + @ViewChild('sidenav') + set sidenavMenu(menu: MatSidenav) { + setTimeout(() => { + if (menu) { + this.sideMenu = menu; + this.subscribeToTogglingMenu(); + } + }, 0); + } + + @ViewChild('videoContainer', { static: false, read: ElementRef }) + set videoContainer(container: ElementRef) { + setTimeout(() => { + if (container && !this.toolbarTemplate) { + container.nativeElement.style.height = '100%'; + container.nativeElement.style.minHeight = '100%'; + this.layoutService.update(); + } + }, 0); + } + + @ViewChild('container') + set container(container: MatDrawerContainer) { + setTimeout(() => { + if (container) { + this.drawer = container; + this.drawer._contentMarginChanges.subscribe(() => { + setTimeout(() => { + this.stopUpdateLayoutInterval(); + this.layoutService.update(); + this.drawer.autosize = false; + }, 250); + }); + } + }, 0); + } + + @ViewChild('layoutContainer') + set layoutContainer(container: ElementRef) { + setTimeout(async () => { + if (container) { + // Apply background from storage when layout container is in DOM + await this.backgroundService.applyBackgroundFromStorage(); + } + }, 0); + } + + async ngOnInit() { + this.room = this.openviduService.getRoom(); + this.onRoomCreated.emit(this.room); + this.participantService.setLocalParticipant(this.room.localParticipant); + + // this.subscribeToCaptionLanguage(); + this.subcribeToActiveSpeakersChanged(); + this.subscribeToParticipantConnected(); + this.subscribeToTrackSubscribed(); + this.subscribeToTrackUnsubscribed(); + this.subscribeToParticipantDisconnected(); + this.subscribeToParticipantMetadataChanged(); + + // this.subscribeToParticipantNameChanged(); + this.subscribeToDataMessage(); + this.subscribeToReconnection(); + + if (this.libService.isRecordingEnabled()) { + // this.subscribeToRecordingEvents(); + } + + if (this.libService.isBroadcastingEnabled()) { + // this.subscribeToBroadcastingEvents(); + } + try { + await this.participantService.connectLocalParticipant(); + this.cd.markForCheck(); + this.loading = false; + this.onParticipantCreated.emit(this.participantService.getLocalParticipant()); + } catch (error) { + this.log.e('There was an error connecting to the room:', error.code, error.message); + this.actionService.openDialog(this.translateService.translate('ERRORS.SESSION'), error?.error || error?.message || error); + } + } + subcribeToActiveSpeakersChanged() { + this.room.on(RoomEvent.ActiveSpeakersChanged, (speakers: Participant[]) => { + this.participantService.setSpeaking(speakers); + }); + } + + async ngOnDestroy() { + if(this.shouldDisconnectRoomWhenComponentIsDestroyed) { + await this.disconnectRoom(); + } + this.room.removeAllListeners(); + this.participantService.clear(); + // this.room = undefined; + if (this.menuSubscription) this.menuSubscription.unsubscribe(); + if (this.layoutWidthSubscription) this.layoutWidthSubscription.unsubscribe(); + // if (this.captionLanguageSubscription) this.captionLanguageSubscription.unsubscribe(); + } + + async disconnectRoom() { + // Mark session as disconnected for avoiding to do it again in ngOnDestroy + this.shouldDisconnectRoomWhenComponentIsDestroyed = false; + await this.openviduService.disconnectRoom(); + } + + private subscribeToTogglingMenu() { + this.sideMenu.openedChange.subscribe(() => { + this.stopUpdateLayoutInterval(); + this.layoutService.update(); + }); + + this.sideMenu.openedStart.subscribe(() => { + this.startUpdateLayoutInterval(); + }); + + this.sideMenu.closedStart.subscribe(() => { + this.startUpdateLayoutInterval(); + }); + + this.menuSubscription = this.panelService.panelStatusObs.pipe(skip(1)).subscribe((ev: PanelStatusInfo) => { + if (this.sideMenu) { + this.settingsPanelOpened = ev.isOpened && ev.panelType === PanelType.SETTINGS; + + if (this.sideMenu.opened && ev.isOpened) { + if (ev.panelType === PanelType.SETTINGS || ev.previousPanelType === PanelType.SETTINGS) { + // Switch from SETTINGS to another panel and vice versa. + // As the SETTINGS panel will be bigger than others, the sidenav container must be updated. + // Setting autosize to 'true' allows update it. + this.drawer.autosize = true; + this.startUpdateLayoutInterval(); + } + } + ev.isOpened ? this.sideMenu.open() : this.sideMenu.close(); + } + }); + } + + private subscribeToLayoutWidth() { + this.layoutWidthSubscription = this.layoutService.layoutWidthObs.subscribe((width) => { + this.sidenavMode = width <= this.SIDENAV_WIDTH_LIMIT_MODE ? SidenavMode.OVER : SidenavMode.SIDE; + }); + } + + private subscribeToParticipantConnected() { + this.room.on(RoomEvent.ParticipantConnected, (participant: RemoteParticipant) => { + this.participantService.addRemoteParticipant(participant); + }); + } + + /** + * The LocalParticipant has subscribed to a new track because of the RoomConnectionOptions has beed set with autosubscribe = 'true'. + * The LocalParticipant will subscribe to all tracks after joining. + */ + private subscribeToTrackSubscribed() { + // this.room.on(RoomEvent.TrackPublished, (publication: RemoteTrackPublication, participant: RemoteParticipant) => { + // console.warn("NEW TrackPublished", participant); + // console.warn("NEW TrackPublished", publication); + // }); + this.room.on( + RoomEvent.TrackSubscribed, + (track: RemoteTrack, publication: RemoteTrackPublication, participant: RemoteParticipant) => { + const isScreenTrack = track.source === Track.Source.ScreenShare; + this.participantService.addRemoteParticipant(participant); + if (isScreenTrack) { + // Set all videos to normal size when a new screen is being shared + this.participantService.resetMyStreamsToNormalSize(); + this.participantService.resetRemoteStreamsToNormalSize(); + this.participantService.toggleRemoteVideoPinned(track.sid); + if (track.sid) this.participantService.setScreenTrackPublicationDate(participant.sid, track.sid, new Date().getTime()); + } + // if (this.openviduService.isSttReady() && this.captionService.areCaptionsEnabled() && isCameraType) { + // // Only subscribe to STT when is ready and stream is CAMERA type and it is a remote stream + // try { + // await this.openviduService.subscribeStreamToStt(event.stream, lang); + // } catch (error) { + // this.log.e('Error subscribing from STT: ', error); + // // I assume the only reason of an STT error is a STT crash. + // // It must be subscribed to all remotes again + // // await this.openviduService.unsubscribeRemotesFromSTT(); + // await this.openviduService.subscribeRemotesToSTT(lang); + // } + // } + } + ); + } + + /** + * The LocalParticipant has unsubscribed from a track. + */ + private subscribeToTrackUnsubscribed() { + this.room.on( + RoomEvent.TrackUnsubscribed, + (track: RemoteTrack, publication: RemoteTrackPublication, participant: RemoteParticipant) => { + this.log.d('TrackUnSubscribed', track, participant); + // TODO: Check if this is the last track of the participant before removing it + const isScreenTrack = track.source === Track.Source.ScreenShare; + if (isScreenTrack) { + if (track.sid) this.participantService.setScreenTrackPublicationDate(participant.sid, track.sid, -1); + this.participantService.resetMyStreamsToNormalSize(); + this.participantService.resetRemoteStreamsToNormalSize(); + // Set last screen track shared to pinned size + this.participantService.setLastScreenPinned(); + } + + if (track.sid) this.participantService.removeRemoteParticipantTrack(participant, track.sid); + // if (this.openviduService.isSttReady() && this.captionService.areCaptionsEnabled() && isRemoteConnection && isCameraType) { + // try { + // await this.session.unsubscribeFromSpeechToText(event.stream); + // } catch (error) { + // this.log.e('Error unsubscribing from STT: ', error); + // } + // } + } + ); + } + + private subscribeToParticipantDisconnected() { + this.room.on(RoomEvent.ParticipantDisconnected, (participant: RemoteParticipant) => { + this.participantService.removeRemoteParticipant(participant.sid); + }); + } + + private subscribeToParticipantMetadataChanged() { + this.room.on( + RoomEvent.ParticipantMetadataChanged, + (metadata: string | undefined, participant: RemoteParticipant | LocalParticipant) => { + console.log('ParticipantMetadataChanged', participant); + } + ); + } + + // private subscribeToCaptionLanguage() { + // this.captionLanguageSubscription = this.captionService.captionLangObs.subscribe(async (langOpt) => { + // if (this.captionService.areCaptionsEnabled()) { + // // Unsubscribe all streams from speech to text and re-subscribe with new language + // this.log.d('Re-subscribe from STT because of language changed to ', langOpt.lang); + // await this.openviduService.unsubscribeRemotesFromSTT(); + // await this.openviduService.subscribeRemotesToSTT(langOpt.lang); + // } + // }); + // } + + // private subscribeToParticipantNameChanged() { + // this.room.on(RoomEvent.ParticipantNameChanged, (name: string, participant: RemoteParticipant | LocalParticipant) => { + // console.log('ParticipantNameChanged', participant); + // }); + // } + + private subscribeToDataMessage() { + this.room.on( + RoomEvent.DataReceived, + (payload: Uint8Array, participant?: RemoteParticipant, _?: DataPacket_Kind, topic?: string) => { + const event = JSON.parse(new TextDecoder().decode(payload)); + this.log.d(`Data event received: ${topic}`); + switch (topic) { + case DataTopic.CHAT: + const participantName = participant?.identity || 'Unknown'; + this.chatService.addRemoteMessage(event.message, participantName); + break; + case DataTopic.RECORDING_STARTING: + this.log.d('Recording is starting', event); + this.recordingService.setRecordingStarting(); + break; + case DataTopic.RECORDING_STARTED: + this.log.d('Recording has been started', event); + this.recordingService.setRecordingStarted(event); + break; + case DataTopic.RECORDING_STOPPING: + this.log.d('Recording is stopping', event); + this.recordingService.setRecordingStopping(); + break; + case DataTopic.RECORDING_STOPPED: + this.log.d('RECORDING_STOPPED', event); + this.recordingService.setRecordingStopped(event); + break; + + case DataTopic.RECORDING_DELETED: + this.log.d('RECORDING_DELETED', event); + this.recordingService.deleteRecording(event); + break; + + case DataTopic.RECORDING_FAILED: + this.log.d('RECORDING_FAILED', event); + this.recordingService.setRecordingFailed(event.error); + break; + + case DataTopic.BROADCASTING_STARTING: + this.broadcastingService.setBroadcastingStarting(); + break; + case DataTopic.BROADCASTING_STARTED: + this.log.d('Broadcasting has been started', event); + this.broadcastingService.setBroadcastingStarted(event); + break; + + case DataTopic.BROADCASTING_STOPPING: + this.broadcastingService.setBroadcastingStopping(); + break; + case DataTopic.BROADCASTING_STOPPED: + this.broadcastingService.setBroadcastingStopped(); + break; + + case DataTopic.BROADCASTING_FAILED: + this.broadcastingService.setBroadcastingFailed(event.error); + break; + + case DataTopic.ROOM_STATUS: + const { recordingList, isRecordingStarted, isBroadcastingStarted, broadcastingId } = event as RoomStatusData; + + this.recordingService.setRecordingList(recordingList); + if (isRecordingStarted) { + this.recordingService.setRecordingStarted(); + } + if (isBroadcastingStarted) { + this.broadcastingService.setBroadcastingStarted(broadcastingId); + } + + default: + break; + } + } + ); + } + + private subscribeToReconnection() { + this.room.on(RoomEvent.Reconnecting, () => { + this.log.w('Connection lost: Reconnecting'); + this.actionService.openDialog( + this.translateService.translate('ERRORS.CONNECTION'), + this.translateService.translate('ERRORS.RECONNECT'), + false + ); + }); + this.room.on(RoomEvent.Reconnected, () => { + this.log.w('Connection lost: Reconnected'); + this.actionService.closeDialog(); + }); + + this.room.on(RoomEvent.Disconnected, async (reason: DisconnectReason | undefined) => { + if (reason === DisconnectReason.SERVER_SHUTDOWN) { + this.log.e('Room Disconnected', reason); + this.actionService.openDialog( + this.translateService.translate('ERRORS.CONNECTION'), + this.translateService.translate('ERRORS.RECONNECT'), + false + ); + } + // await this.disconnectRoom(); + }); + } + + private startUpdateLayoutInterval() { + this.updateLayoutInterval = setInterval(() => { + this.layoutService.update(); + }, 50); + } + + private stopUpdateLayoutInterval() { + if (this.updateLayoutInterval) { + clearInterval(this.updateLayoutInterval); + } + } +} diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/audio-devices/audio-devices.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/audio-devices/audio-devices.component.html new file mode 100644 index 00000000..d97865b2 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/audio-devices/audio-devices.component.html @@ -0,0 +1,55 @@ +
+ + + + + + {{ 'PANEL.SETTINGS.DISABLED_AUDIO' | translate }} + {{ microphoneSelected.label }} + + + {{ microphone.label }} + + + + +
+
+ + {{ 'PREJOIN.NO_AUDIO_DEVICE' | translate }} +
+
+
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/audio-devices/audio-devices.component.scss b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/audio-devices/audio-devices.component.scss new file mode 100644 index 00000000..880edee3 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/audio-devices/audio-devices.component.scss @@ -0,0 +1,84 @@ +:host { + --ov-light-color-darker: color-mix(in srgb, var(--ov-light-color), #000 15%); + + .device-container-element { + border-radius: var(--ov-panel-radius); + border: 1px solid var(--ov-light-color-darker); + } + #audio-devices-form { + width: 100%; + height: 50px; + } + + #audio-devices-not-found { + font-size: 13px; + } + + #microphone-button { + color: var(--ov-secondary-color); + } + + ::ng-deep .mat-mdc-text-field-wrapper, + ::ng-deep .mat-mdc-form-field-flex, + ::ng-deep .mat-mdc-select-trigger { + height: 50px !important; + } + + ::ng-deep .mat-mdc-form-field-subscript-wrapper { + display: none !important; + } + + ::ng-deep .mat-mdc-text-field-wrapper { + padding-left: 0px; + padding-right: 10px; + background-color: var(--ov-panel-background) !important; + border-radius: var(--ov-panel-radius); + } + ::ng-deep .mdc-button--unelevated { + border-top-left-radius: var(--ov-panel-radius); + border-bottom-left-radius: var(--ov-panel-radius); + border-bottom-right-radius: 0px !important; + border-top-right-radius: 0px !important; + background-color: var(--ov-light-color-darker) !important; + width: 48px !important; + min-width: 48px !important; + padding: 0; + height: 50px; + } + + ::ng-deep .mat-mdc-unelevated-button > .mat-icon { + height: 24px; + width: 24px; + font-size: 24px !important; + margin: auto; + } + + ::ng-deep .mat-mdc-form-field-infix { + padding: 0px !important; + min-height: 100%; + } + + .selected-text { + padding-left: 5px; + } + + .mat-icon { + vertical-align: middle; + display: inline-flex; + } + + ::ng-deep .mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-line-ripple::before { + border: 0px !important; + } + + ::ng-deep .mat-mdc-button-touch-target { + border-radius: var(--ov-panel-radius) !important; + } + + .warn-btn { + color: var(--ov-warn-color) !important; + } +} +::ng-deep .mat-mdc-select-panel { + background-color: var(--ov-panel-background) !important; +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/audio-devices/audio-devices.component.spec.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/audio-devices/audio-devices.component.spec.ts diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/audio-devices/audio-devices.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/audio-devices/audio-devices.component.ts new file mode 100644 index 00000000..02ad7f9f --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/audio-devices/audio-devices.component.ts @@ -0,0 +1,92 @@ +import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { CustomDevice } from '../../../models/device.model'; +import { DeviceService } from '../../../services/device/device.service'; +import { ParticipantService } from '../../../services/participant/participant.service'; +import { StorageService } from '../../../services/storage/storage.service'; +import { ParticipantModel } from '../../../models/participant.model'; + +/** + * @internal + */ +@Component({ + selector: 'ov-audio-devices-select', + templateUrl: './audio-devices.component.html', + styleUrls: ['./audio-devices.component.scss'] +}) +export class AudioDevicesComponent implements OnInit, OnDestroy { + @Output() onAudioDeviceChanged = new EventEmitter(); + @Output() onAudioEnabledChanged = new EventEmitter(); + + microphoneStatusChanging: boolean; + hasAudioDevices: boolean; + isMicrophoneEnabled: boolean; + microphoneSelected: CustomDevice | undefined; + microphones: CustomDevice[] = []; + private localParticipantSubscription: Subscription; + + constructor( + private deviceSrv: DeviceService, + private storageSrv: StorageService, + private participantService: ParticipantService + ) {} + + async ngOnInit() { + this.subscribeToParticipantMediaProperties(); + this.hasAudioDevices = this.deviceSrv.hasAudioDeviceAvailable(); + if (this.hasAudioDevices) { + this.microphones = this.deviceSrv.getMicrophones(); + this.microphoneSelected = this.deviceSrv.getMicrophoneSelected(); + } + + this.isMicrophoneEnabled = this.participantService.isMyMicrophoneEnabled(); + } + + ngOnDestroy() { + this.microphones = []; + if (this.localParticipantSubscription) this.localParticipantSubscription.unsubscribe(); + } + + async toggleMic(event: any) { + event.stopPropagation(); + this.microphoneStatusChanging = true; + this.isMicrophoneEnabled = !this.isMicrophoneEnabled; + await this.participantService.setMicrophoneEnabled(this.isMicrophoneEnabled); + this.microphoneStatusChanging = false; + this.storageSrv.setMicrophoneEnabled(this.isMicrophoneEnabled); + this.onAudioEnabledChanged.emit(this.isMicrophoneEnabled); + } + + async onMicrophoneSelected(event: any) { + const device: CustomDevice = event?.value; + if (this.deviceSrv.needUpdateAudioTrack(device)) { + this.microphoneStatusChanging = true; + await this.participantService.switchMicrophone(device.device); + this.deviceSrv.setMicSelected(device.device); + this.microphoneSelected = this.deviceSrv.getMicrophoneSelected(); + this.microphoneStatusChanging = false; + this.onAudioDeviceChanged.emit(this.microphoneSelected); + } + } + + /** + * @internal + * Compare two devices to check if they are the same. Used by the mat-select + */ + compareObjectDevices(o1: CustomDevice, o2: CustomDevice): boolean { + return o1.label === o2.label; + } + + /** + * This subscription is necessary to update the microphone status when the user changes it from toolbar and + * the settings panel is opened. With this, the microphone status is updated in the settings panel. + */ + private subscribeToParticipantMediaProperties() { + this.localParticipantSubscription = this.participantService.localParticipant$.subscribe((p: ParticipantModel | undefined) => { + if (p) { + this.isMicrophoneEnabled = p.isMicrophoneEnabled; + this.storageSrv.setMicrophoneEnabled(this.isMicrophoneEnabled); + } + }); + } +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/captions/captions.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/captions/captions.component.html similarity index 99% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/captions/captions.component.html rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/captions/captions.component.html index 29e9d577..6a01c754 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/captions/captions.component.html +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/captions/captions.component.html @@ -13,7 +13,7 @@
- +
{{ 'PANEL.SETTINGS.LANGUAGE' | translate }}
+ + + + diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/lang-selector/lang-selector.component.scss b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/lang-selector/lang-selector.component.scss new file mode 100644 index 00000000..479172cc --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/lang-selector/lang-selector.component.scss @@ -0,0 +1,12 @@ +$ov-light-color-lighter: color-mix(in srgb, var(--ov-light-color), #fff 40%); + +.lang-button { + background-color: var(--ov-secondary-color) !important; + color: var(--ov-text-color); +} +::ng-deep .mat-mdc-menu-panel { + border-radius: var(--ov-panel-radius) !important; + background-color: var(--ov-panel-background) !important; + border: 1px solid $ov-light-color-lighter !important; + box-shadow: 1px 1px 5px 0px rgba(0, 0, 0, 0.2) !important; +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/lang-selector/lang-selector.component.spec.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/lang-selector/lang-selector.component.spec.ts diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/lang-selector/lang-selector.component.ts similarity index 63% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/lang-selector/lang-selector.component.ts index 1e620de6..2aa021d0 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/lang-selector/lang-selector.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, OnInit, Output, ViewChild, EventEmitter, OnDestroy } from '@angular/core'; +import { Component, OnInit, Output, ViewChild, EventEmitter, Input, OnDestroy } from '@angular/core'; import { MatMenuTrigger } from '@angular/material/menu'; import { MatSelect } from '@angular/material/select'; import { StorageService } from '../../../services/storage/storage.service'; @@ -12,15 +12,17 @@ import { Subscription } from 'rxjs'; @Component({ selector: 'ov-lang-selector', templateUrl: './lang-selector.component.html', - styleUrls: ['./lang-selector.component.css'] + styleUrls: ['./lang-selector.component.scss'] }) -export class LangSelectorComponent implements OnInit, AfterViewInit, OnDestroy { - @Output() onLangSelectorClicked = new EventEmitter(); - langSelected: LangOption | undefined; +export class LangSelectorComponent implements OnInit, OnDestroy { + /** + * @internal + */ + @Input() compact: boolean; + @Output() onLangChanged: EventEmitter = new EventEmitter(); + langSelected: LangOption; languages: LangOption[] = []; - private langSub: Subscription; - /** * @ignore */ @@ -31,7 +33,12 @@ export class LangSelectorComponent implements OnInit, AfterViewInit, OnDestroy { */ @ViewChild(MatSelect) matSelect: MatSelect; - constructor(private translateService: TranslateService, private storageSrv: StorageService) {} + private langSub: Subscription; + + constructor( + private translateService: TranslateService, + private storageSrv: StorageService + ) {} ngOnInit(): void { this.subscribeToLangSelected(); @@ -42,15 +49,6 @@ export class LangSelectorComponent implements OnInit, AfterViewInit, OnDestroy { this.langSub?.unsubscribe(); } - ngAfterViewInit() { - this.menuTrigger?.menuOpened.subscribe(() => { - this.onLangSelectorClicked.emit(); - }); - this.matSelect?.openedChange.subscribe(() => { - this.onLangSelectorClicked.emit(); - }); - } - onLangSelected(lang: string) { this.translateService.setLanguage(lang); this.storageSrv.setLang(lang); @@ -59,6 +57,7 @@ export class LangSelectorComponent implements OnInit, AfterViewInit, OnDestroy { subscribeToLangSelected() { this.langSub = this.translateService.langSelectedObs.subscribe((lang) => { this.langSelected = lang; + this.onLangChanged.emit(lang); }); } } diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/participant-name-input/participant-name-input.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/participant-name-input/participant-name-input.component.html new file mode 100644 index 00000000..0ec08c2a --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/participant-name-input/participant-name-input.component.html @@ -0,0 +1,19 @@ +
+ + + + + + +
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/participant-name-input/participant-name-input.component.scss b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/participant-name-input/participant-name-input.component.scss new file mode 100644 index 00000000..df5157e1 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/participant-name-input/participant-name-input.component.scss @@ -0,0 +1,62 @@ +:host { + --ov-light-color-darker: color-mix(in srgb, var(--ov-light-color), #000 15%); + + #name-input-container { + height: 70px; + border-radius: var(--ov-panel-radius); + } + + #name-input-container mat-form-field { + width: 100%; + color: var(--ov-secondary-color); + } + + ::ng-deep .mat-mdc-form-field-infix { + display: inline-flex; + padding: 0px !important; + } + ::ng-deep .mat-mdc-text-field-wrapper { + padding: 0; + height: 70px; + background-color: var(--ov-panel-background) !important; + + } + ::ng-deep .mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-line-ripple::before { + border: 0px !important; + } + ::ng-deep .mdc-button--unelevated { + border-top-left-radius: var(--ov-panel-radius) !important; + border-bottom-left-radius: var(--ov-panel-radius) !important; + border-bottom-right-radius: 0px !important; + border-top-right-radius: 0px !important; + background-color: var(--ov-light-color-darker) !important; + width: 48px !important; + min-width: 48px !important; + padding: 0; + height: 70px; + } + + .error { + ::ng-deep .mdc-button--unelevated { + background-color: var(--ov-warn-color) !important; + } + } + + ::ng-deep .mat-mdc-unelevated-button > .mat-icon { + height: 24px; + width: 24px; + font-size: 24px !important; + margin: auto; + color: var(--ov-secondary-color) !important; + } + + input { + padding-left: 10px !important; + border-top-right-radius: var(--ov-panel-radius) !important; + border-bottom-right-radius: var(--ov-panel-radius) !important; + border-bottom-left-radius: 0px !important; + border-top-left-radius: 0px !important; + border: 1px solid var(--ov-light-color-darker); + color: var(--ov-panel-text-color); + } +} diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/participant-name-input/participant-name-input.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/participant-name-input/participant-name-input.component.spec.ts new file mode 100644 index 00000000..1632ae33 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/participant-name-input/participant-name-input.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ParticipantNameInputComponent } from './participant-name-input.component'; + +describe('ParticipantNameInputComponent', () => { + let component: ParticipantNameInputComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ParticipantNameInputComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ParticipantNameInputComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/participant-name-input/participant-name-input.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/participant-name-input/participant-name-input.component.ts new file mode 100644 index 00000000..40b3107e --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/participant-name-input/participant-name-input.component.ts @@ -0,0 +1,77 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { ParticipantService } from '../../../services/participant/participant.service'; +import { StorageService } from '../../../services/storage/storage.service'; + +/** + * @internal + */ +@Component({ + selector: 'ov-participant-name-input', + templateUrl: './participant-name-input.component.html', + styleUrls: ['./participant-name-input.component.scss'] +}) +export class ParticipantNameInputComponent implements OnInit { + name: string; + localParticipantSubscription: Subscription; + @Input() isPrejoinPage: boolean; + @Input() error: boolean; + @Output() onNameUpdated = new EventEmitter(); + @Output() onEnterPressed = new EventEmitter(); + + constructor( + private participantService: ParticipantService, + private storageSrv: StorageService + ) {} + + ngOnInit(): void { + this.subscribeToParticipantProperties(); + const myName = this.participantService.getMyName(); + const storedName = this.storageSrv.getParticipantName(); + + this.name = myName ?? storedName ?? this.generateRandomName(); + + if (!myName && !storedName) { + this.storageSrv.setParticipantName(this.name); + } + + this.onNameUpdated.emit(this.name); + } + + /** + * As updating name requires that the participant has the `canUpdateOwnMetadata` to true in server side, which is a little bit insecure, + * we decided to not allow this feature for now. + */ + updateName() { + if (this.isPrejoinPage) { + this.name = this.name ?? this.participantService.getMyName(); + // this.participantService.setMyName(this.name); + this.storageSrv.setParticipantName(this.name); + this.onNameUpdated.emit(this.name); + } + } + + /** + * @ignore + */ + eventKeyPress(event) { + // Pressed 'Enter' key + if (event && event.keyCode === 13 && this.name) { + event.preventDefault(); + this.updateName(); + this.onEnterPressed.emit(); + } + } + + private subscribeToParticipantProperties() { + // this.localParticipantSubscription = this.participantService.localParticipant$.subscribe((p: ParticipantModel | undefined) => { + // if (p) { + // this.name = p.name; + // } + // }); + } + + private generateRandomName(): string { + return 'OpenVidu_User_' + Math.floor(Math.random() * 100); + } +} diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/video-devices/video-devices.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/video-devices/video-devices.component.html new file mode 100644 index 00000000..c29a6bc8 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/video-devices/video-devices.component.html @@ -0,0 +1,40 @@ +
+ + + + + {{ 'PANEL.SETTINGS.DISABLED_VIDEO' | translate }} + {{ cameraSelected.label }} + + + {{ camera.label }} + + + + +
+
+ + {{ 'PREJOIN.NO_VIDEO_DEVICE' | translate }} +
+
+
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/video-devices/video-devices.component.scss b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/video-devices/video-devices.component.scss new file mode 100644 index 00000000..bd863939 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/video-devices/video-devices.component.scss @@ -0,0 +1,83 @@ +:host { + --ov-light-color-darker: color-mix(in srgb, var(--ov-light-color), #000 15%); + + .device-container-element { + border-radius: var(--ov-panel-radius); + border: 1px solid var(--ov-light-color-darker); + } + #video-devices-form { + width: 100%; + height: 50px; + } + + #video-devices-not-found { + font-size: 13px; + } + + #camera-button { + color: var(--ov-secondary-color); + } + + ::ng-deep .mat-mdc-text-field-wrapper, + ::ng-deep .mat-mdc-form-field-flex, + ::ng-deep .mat-mdc-select-trigger { + height: 50px !important; + } + + ::ng-deep .mat-mdc-form-field-subscript-wrapper { + display: none !important; + } + + ::ng-deep .mat-mdc-text-field-wrapper { + padding-left: 0px; + padding-right: 10px; + background-color: var(--ov-panel-background) !important; + border-radius: var(--ov-panel-radius); + } + ::ng-deep .mdc-button--unelevated { + border-top-left-radius: var(--ov-panel-radius); + border-bottom-left-radius: var(--ov-panel-radius); + border-bottom-right-radius: 0px !important; + border-top-right-radius: 0px !important; + background-color: var(--ov-light-color-darker) !important; + width: 48px !important; + min-width: 48px !important; + padding: 0; + height: 50px; + } + + ::ng-deep .mat-mdc-unelevated-button > .mat-icon { + height: 24px; + width: 24px; + font-size: 24px !important; + margin: auto; + } + ::ng-deep .mat-mdc-form-field-infix { + padding: 0px !important; + min-height: 100%; + } + + .selected-text { + padding-left: 5px; + } + + .mat-icon { + vertical-align: middle; + display: inline-flex; + } + + ::ng-deep .mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-line-ripple::before { + border: 0px !important; + } + + ::ng-deep .mat-mdc-button-touch-target { + border-radius: var(--ov-panel-radius) !important; + } + + .warn-btn { + color: var(--ov-warn-color) !important; + } +} +::ng-deep .mat-mdc-select-panel { + background-color: var(--ov-panel-background) !important; +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/video-devices/video-devices.component.spec.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/video-devices/video-devices.component.spec.ts diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/video-devices/video-devices.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/video-devices/video-devices.component.ts new file mode 100644 index 00000000..cf25c72b --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/settings/video-devices/video-devices.component.ts @@ -0,0 +1,120 @@ +import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { CustomDevice } from '../../../models/device.model'; +import { DeviceService } from '../../../services/device/device.service'; +import { PanelService } from '../../../services/panel/panel.service'; +import { ParticipantService } from '../../../services/participant/participant.service'; +import { StorageService } from '../../../services/storage/storage.service'; +import { VirtualBackgroundService } from '../../../services/virtual-background/virtual-background.service'; +import { ParticipantModel } from '../../../models/participant.model'; + +/** + * @internal + */ +@Component({ + selector: 'ov-video-devices-select', + templateUrl: './video-devices.component.html', + styleUrls: ['./video-devices.component.scss'] +}) +export class VideoDevicesComponent implements OnInit, OnDestroy { + @Output() onVideoDeviceChanged = new EventEmitter(); + @Output() onVideoEnabledChanged = new EventEmitter(); + + cameraStatusChanging: boolean; + isCameraEnabled: boolean; + cameraSelected: CustomDevice | undefined; + hasVideoDevices: boolean; + cameras: CustomDevice[] = []; + localParticipantSubscription: Subscription; + + constructor( + private panelService: PanelService, + private storageSrv: StorageService, + private deviceSrv: DeviceService, + private participantService: ParticipantService, + private backgroundService: VirtualBackgroundService + ) {} + + async ngOnInit() { + this.subscribeToParticipantMediaProperties(); + + this.hasVideoDevices = this.deviceSrv.hasVideoDeviceAvailable(); + if (this.hasVideoDevices) { + this.cameras = this.deviceSrv.getCameras(); + this.cameraSelected = this.deviceSrv.getCameraSelected(); + } + + this.isCameraEnabled = this.participantService.isMyCameraEnabled(); + } + + async ngOnDestroy() { + this.cameras = []; + if (this.localParticipantSubscription) this.localParticipantSubscription.unsubscribe(); + } + + async toggleCam(event: any) { + event.stopPropagation(); + this.cameraStatusChanging = true; + this.isCameraEnabled = !this.isCameraEnabled; + await this.participantService.setCameraEnabled(this.isCameraEnabled); + this.storageSrv.setCameraEnabled(this.isCameraEnabled); + this.onVideoEnabledChanged.emit(this.isCameraEnabled); + this.cameraStatusChanging = false; + } + + async onCameraSelected(event: any) { + const device: CustomDevice = event?.value; + + // Is New deviceId different from the old one? + if (this.deviceSrv.needUpdateVideoTrack(device)) { + // const mirror = this.deviceSrv.cameraNeedsMirror(device.device); + // Reapply Virtual Background to new Publisher if necessary + // const backgroundSelected = this.backgroundService.backgroundSelected.getValue(); + // const isBackgroundApplied = this.backgroundService.isBackgroundApplied(); + + // if (isBackgroundApplied) { + // await this.backgroundService.removeBackground(); + // } + // const pp: PublisherProperties = { videoSource: device.device, audioSource: false, mirror }; + // const publisher = this.participantService.getMyCameraPublisher(); + // await this.openviduService.replaceCameraTrack(publisher, pp); + + this.cameraStatusChanging = true; + + await this.participantService.switchCamera(device.device); + + // if (isBackgroundApplied) { + // const bgSelected = this.backgroundService.backgrounds.find((b) => b.id === backgroundSelected); + // if (bgSelected) { + // await this.backgroundService.applyBackground(bgSelected); + // } + // } + + this.deviceSrv.setCameraSelected(device.device); + this.cameraSelected = this.deviceSrv.getCameraSelected(); + this.cameraStatusChanging = false; + this.onVideoDeviceChanged.emit(this.cameraSelected); + } + } + + /** + * @internal + * Compare two devices to check if they are the same. Used by the mat-select + */ + compareObjectDevices(o1: CustomDevice, o2: CustomDevice): boolean { + return o1.label === o2.label; + } + + /** + * This subscription is necessary to update the camera status when the user changes it from toolbar and + * the settings panel is opened. With this, the camera status is updated in the settings panel. + */ + private subscribeToParticipantMediaProperties() { + this.localParticipantSubscription = this.participantService.localParticipant$.subscribe((p: ParticipantModel | undefined) => { + if (p) { + this.isCameraEnabled = p.isCameraEnabled; + this.storageSrv.setCameraEnabled(this.isCameraEnabled); + } + }); + } +} diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.html new file mode 100644 index 00000000..9159ca8f --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.html @@ -0,0 +1,80 @@ +
+
+
+ {{ _track.participant.name }} + _SCREEN +
+
+ +
+ +
+ + + +
+ + + +
+ +
+
+ + + +
+
+
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.md b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.md new file mode 100644 index 00000000..0f641882 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.md @@ -0,0 +1,23 @@ + + + +## Structural Directives + +With these kind of directives you can replace the default **StreamComponent** with your own component. You can also add your own components to the default ones. + +| **Directive** | **Reference** | +| :------------: | :--------------: | +| **\*ovStream** | [StreamDirective](../directives/StreamDirective.html) | + + + +## API Directives +With the following directives you can modify the default User Interface with the aim of fully customizing your videoconference application. + + +| **Parameter** | **Type** | **Reference** | +|:--------------------------------: | :-------: | :---------------------------------------------: | +| **displayParticipantName** | `boolean` | [StreamDisplayParticipantNameDirective](../directives/StreamDisplayParticipantNameDirective.html) | +| **displayAudioDetection** | `boolean` | [StreamDisplayAudioDetectionDirective](../directives/StreamDisplayAudioDetectionDirective.html) | +| **videoControls** | `boolean` | [StreamVideoControlsDirective](../directives/StreamVideoControlsDirective.html) | + diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.scss b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.scss new file mode 100644 index 00000000..924dea58 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.scss @@ -0,0 +1,105 @@ +:host { + /* Fixes layout bug. The OV_root is created with the entire layout width and it has a weird UX behaviour */ + .no-size { + height: 0px !important; + width: 0px !important; + } + + .participant-name { + padding: 0px; + position: absolute; + z-index: 999; + border-radius: var(--ov-video-radius); + color: var(--ov-text-color); + font-family: 'Roboto', 'RobotoDraft', Helvetica, Arial, sans-serif; + } + .participant-name-container { + background-color: var(--ov-secondary-color); + padding: 5px; + color: var(--ov-text-color); + font-weight: bold; + border-radius: var(--ov-video-radius); + } + + #audio-wave-container { + position: absolute; + right: 0; + z-index: 999; + padding: 5px; + } + + .fullscreen { + top: 40px; + } + + .flex-container { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + button { + margin: auto; + } + } + + .active-btn { + color: var(--ov-tertiary-color) !important; + } + .muted-btn { + color: var(--ov-warn-color) !important; + } + + .stream-video-controls { + background-color: var(--ov-secondary-color); + border-radius: var(--ov-video-radius); + width: fit-content; + height: 50px; + opacity: 0.5; + position: absolute; + display: inline-grid; + z-index: 9999; + margin: auto; + bottom: 0; + right: 0; + left: 0; + top: 0; + // border: 2px solid var(--ov-text-color); + button { + color: var(--ov-text-color); + } + } + + .stream-video-controls:hover { + opacity: 1; + } + + .status-icons { + position: absolute; + bottom: 0; + z-index: 999; + left: 0; + line-height: 0; + + #status-mic, + #status-muted-forcibly { + color: var(--ov-warn-color); + font-size: 24px; + margin: 5px; + } + } + + /* Contains the video element, used to fix video letter-boxing */ + .OV_stream { + width: 100%; + height: 100%; + position: relative; + overflow: hidden; + background-color: transparent; + border-radius: var(--ov-video-radius); + border: 2px solid var(--ov-primary-color); + box-sizing: border-box; + } + + .OV_stream.speaking { + border-color: var(--ov-tertiary-color); + } +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/stream/stream.component.spec.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.spec.ts similarity index 100% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/stream/stream.component.spec.ts rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.spec.ts diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.ts new file mode 100644 index 00000000..311c54de --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/stream/stream.component.ts @@ -0,0 +1,205 @@ +import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { MatMenuPanel, MatMenuTrigger } from '@angular/material/menu'; +import { Subscription } from 'rxjs'; +import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.service'; +import { OpenViduComponentsConfigService } from '../../services/config/openvidu-components-angular.config.service'; +import { LayoutService } from '../../services/layout/layout.service'; +import { OpenViduService } from '../../services/openvidu/openvidu.service'; +import { ParticipantService } from '../../services/participant/participant.service'; +import { StorageService } from '../../services/storage/storage.service'; +import { Track } from 'livekit-client'; +import { ParticipantTrackPublication } from '../../models/participant.model'; + +/** + * The **StreamComponent** is hosted inside of the {@link LayoutComponent}. + * It is in charge of displaying the participant video stream in the videoconference layout. + */ +@Component({ + selector: 'ov-stream', + templateUrl: './stream.component.html', + styleUrls: ['./stream.component.scss'] +}) +export class StreamComponent implements OnInit, OnDestroy { + /** + * @ignore + */ + @ViewChild(MatMenuTrigger) public menuTrigger: MatMenuTrigger; + + /** + * @ignore + */ + @ViewChild('menu') menu: MatMenuPanel; + + /** + * @ignore + */ + videoTypeEnum = Track.Source; + + /** + * @ignore + */ + _track: ParticipantTrackPublication | undefined; + + /** + * @ignore + */ + isMinimal: boolean = false; + /** + * @ignore + */ + showParticipantName: boolean = true; + /** + * @ignore + */ + showAudioDetection: boolean = true; + /** + * @ignore + */ + showVideoControls: boolean = true; + /** + * @ignore + */ + showVideo: boolean; + + /** + * @ignore + */ + mouseHovering: boolean = false; + + /** + * @ignore + */ + hoveringTimeout: NodeJS.Timeout; + + /** + * @ignore + */ + @ViewChild('streamContainer', { static: false, read: ElementRef }) + set streamContainer(streamContainer: ElementRef) { + setTimeout(() => { + if (streamContainer) { + this._streamContainer = streamContainer; + // This is a workaround for fixing a layout bug which provide a bad UX with each new elements created. + setTimeout(() => { + this.showVideo = true; + }, 100); + } + }, 0); + } + + @Input() + set track(track: ParticipantTrackPublication) { + this._track = track; + } + + private _streamContainer: ElementRef; + private minimalSub: Subscription; + private displayParticipantNameSub: Subscription; + private displayAudioDetectionSub: Subscription; + private videoControlsSub: Subscription; + private readonly HOVER_TIMEOUT = 3000; + + /** + * @ignore + */ + constructor( + private openviduService: OpenViduService, + private layoutService: LayoutService, + private participantService: ParticipantService, + private storageService: StorageService, + private cdkSrv: CdkOverlayService, + private libService: OpenViduComponentsConfigService + ) {} + + ngOnInit() { + this.subscribeToStreamDirectives(); + } + + ngOnDestroy() { + this.cdkSrv.setSelector('body'); + if (this.videoControlsSub) this.videoControlsSub.unsubscribe(); + if (this.displayAudioDetectionSub) this.displayAudioDetectionSub.unsubscribe(); + if (this.displayParticipantNameSub) this.displayParticipantNameSub.unsubscribe(); + if (this.minimalSub) this.minimalSub.unsubscribe(); + } + + /** + * @ignore + */ + toggleVideoPinned() { + const sid = this._track?.trackSid; + if (this._track?.participant) { + if (this._track?.participant.isLocal) { + if (this._track?.participant.isMinimized) { + this.participantService.toggleMyVideoMinimized(sid); + } + this.participantService.toggleMyVideoPinned(sid); + } else { + this.participantService.toggleRemoteVideoPinned(sid); + } + } + this.layoutService.update(); + } + + /** + * @ignore + */ + toggleMinimize() { + const sid = this._track?.trackSid; + if (this._track?.participant && this._track?.participant.isLocal) { + this.participantService.toggleMyVideoMinimized(sid); + this.layoutService.update(); + } + } + + /** + * @ignore + */ + toggleVideoMenu(event) { + if (this.menuTrigger.menuOpen) { + this.menuTrigger.closeMenu(); + return; + } + this.cdkSrv.setSelector('#container-' + this._track?.trackSid); + this.menuTrigger.openMenu(); + } + + /** + * @ignore + */ + toggleMuteForcibly() { + if (this._track?.participant) { + this.participantService.setRemoteMutedForcibly(this._track?.participant.sid, !this._track?.isMutedForcibly); + } + } + + /** + * @ignore + */ + mouseHover(event: MouseEvent) { + event.preventDefault(); + clearTimeout(this.hoveringTimeout); + this.mouseHovering = true; + this.hoveringTimeout = setTimeout(() => { + this.mouseHovering = false; + }, this.HOVER_TIMEOUT); + } + + private subscribeToStreamDirectives() { + this.minimalSub = this.libService.minimal$.subscribe((value: boolean) => { + this.isMinimal = value; + }); + this.displayParticipantNameSub = this.libService.displayParticipantName$.subscribe((value: boolean) => { + this.showParticipantName = value; + // this.cd.markForCheck(); + }); + this.displayAudioDetectionSub = this.libService.displayAudioDetection$.subscribe((value: boolean) => { + this.showAudioDetection = value; + // this.cd.markForCheck(); + }); + this.videoControlsSub = this.libService.streamVideoControls$.subscribe((value: boolean) => { + this.showVideoControls = value; + // this.cd.markForCheck(); + }); + } +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/toolbar/toolbar.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar.component.html similarity index 61% rename from openvidu-components-angular/projects/openvidu-angular/src/lib/components/toolbar/toolbar.component.html rename to openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar.component.html index e2d46847..185193b2 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/toolbar/toolbar.component.html +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar.component.html @@ -1,109 +1,108 @@ - -
+ + + + +
- {{ session.sessionId }} -
+ {{ room.name }} +
radio_button_checked REC - | {{ recordingTime | date : 'H:mm:ss' }} + | {{ recordingTime | date: 'H:mm:ss' }}
sensors LIVE - | {{ broadcastingTime | date : 'H:mm:ss' }}
-
- - - +
- + + + + + + + + + + + - + - + --> @@ -195,11 +192,17 @@ -
-