mediasfu-angular 2.0.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -288,19 +288,21 @@ updateValidated(true);
288
288
 
289
289
  See the following code for the PreJoinPage page logic:
290
290
 
291
- ```javascript
292
- import { Component, Inject, Input, OnInit, Optional } from '@angular/core';
291
+ ```typescript
292
+ import { Component, Inject, Input, OnInit, Optional } from '@angular/core';
293
293
  import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
294
294
  import { CommonModule } from '@angular/common';
295
295
  import { Socket } from 'socket.io-client';
296
296
  import {
297
297
  ConnectSocketType, ShowAlert,
298
298
  ConnectLocalSocketType, ResponseLocalConnection,
299
- ResponseLocalConnectionData, RecordingParams, MeetingRoomParams
299
+ ResponseLocalConnectionData, RecordingParams, MeetingRoomParams,
300
+ CreateMediaSFURoomOptions,JoinMediaSFURoomOptions,
300
301
  } from '../../../@types/types';
301
302
  import { CheckLimitsAndMakeRequest } from '../../../methods/utils/check-limits-and-make-request.service';
302
303
  import { CreateRoomOnMediaSFU } from '../../../methods/utils/create-room-on-media-sfu.service';
303
- import { JoinRoomOnMediaSFUService } from '../../../methods/utils/join-room-on-media-sfu.service';
304
+ import { CreateRoomOnMediaSFUType, JoinRoomOnMediaSFUType, JoinRoomOnMediaSFU } from '../../../methods/utils/join-room-on-media-sfu.service';
305
+ import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
304
306
 
305
307
  export interface JoinLocalEventRoomParameters {
306
308
  eventID: string;
@@ -370,6 +372,10 @@ export interface PreJoinPageOptions {
370
372
  connectMediaSFU?: boolean;
371
373
  parameters: PreJoinPageParameters;
372
374
  credentials?: Credentials;
375
+ returnUI?: boolean;
376
+ noUIPreJoinOptions?: CreateMediaSFURoomOptions | JoinMediaSFURoomOptions;
377
+ createMediaSFURoom?: CreateRoomOnMediaSFUType;
378
+ joinMediaSFURoom?: JoinRoomOnMediaSFUType;
373
379
  }
374
380
 
375
381
  export type PreJoinPageType = (options: PreJoinPageOptions) => HTMLElement;
@@ -454,6 +460,11 @@ export class PreJoinPage implements OnInit {
454
460
  @Input() credentials: Credentials = { apiUserName: 'yourAPIUSERNAME', apiKey: 'yourAPIKEY' };
455
461
  @Input() localLink: string | undefined = "";
456
462
  @Input() connectMediaSFU: boolean | undefined = true;
463
+ @Input() returnUI?: boolean;
464
+ @Input() noUIPreJoinOptions?: CreateMediaSFURoomOptions | JoinMediaSFURoomOptions;
465
+ @Input() createMediaSFURoom?: CreateRoomOnMediaSFUType;
466
+ @Input() joinMediaSFURoom?: JoinRoomOnMediaSFUType;
467
+
457
468
 
458
469
  isCreateMode = false;
459
470
  preJoinForm: FormGroup;
@@ -465,15 +476,23 @@ export class PreJoinPage implements OnInit {
465
476
  localData: ResponseLocalConnectionData | undefined = undefined;
466
477
  initSocket: Socket | undefined = undefined;
467
478
 
479
+ pending = new BehaviorSubject<boolean>(false);
480
+
468
481
  constructor(
469
482
  private fb: FormBuilder,
470
483
  @Optional() @Inject('parameters') injectedParameters: PreJoinPageParameters,
471
484
  @Optional() @Inject('credentials') injectedCredentials: Credentials,
472
485
  @Optional() @Inject('localLink') injectedLocalLink: string,
473
486
  @Optional() @Inject('connectMediaSFU') injectedConnectMediaSFU: boolean,
487
+ @Optional() @Inject('returnUI') injectedReturnUI: boolean,
488
+ @Optional() @Inject('noUIPreJoinOptions') injectedNoUIPreJoinOptions: CreateMediaSFURoomOptions | JoinMediaSFURoomOptions,
489
+ @Optional() @Inject('createMediaSFURoom') injectedCreateMediaSFURoom: CreateRoomOnMediaSFUType,
490
+ @Optional() @Inject('joinMediaSFURoom') injectedJoinMediaSFURoom: JoinRoomOnMediaSFUType,
491
+
492
+
474
493
  private checkLimitsService: CheckLimitsAndMakeRequest,
475
494
  private createRoomService: CreateRoomOnMediaSFU,
476
- private joinRoomService: JoinRoomOnMediaSFUService
495
+ private joinRoomService: JoinRoomOnMediaSFU
477
496
  ) {
478
497
  this.preJoinForm = this.fb.group({
479
498
  name: ['', Validators.required],
@@ -486,12 +505,22 @@ export class PreJoinPage implements OnInit {
486
505
  this.credentials = injectedCredentials || this.credentials;
487
506
  this.localLink = injectedLocalLink || this.localLink;
488
507
  this.connectMediaSFU = injectedConnectMediaSFU !== undefined ? injectedConnectMediaSFU : this.connectMediaSFU;
508
+ this.returnUI = injectedReturnUI !== undefined ? injectedReturnUI : this.returnUI;
509
+ this.noUIPreJoinOptions = injectedNoUIPreJoinOptions || this.noUIPreJoinOptions;
510
+ this.createMediaSFURoom = injectedCreateMediaSFURoom || this.createMediaSFURoom;
511
+ this.joinMediaSFURoom = injectedJoinMediaSFURoom || this.joinMediaSFURoom;
489
512
 
490
513
  }
491
514
 
492
515
  ngOnInit(): void {
516
+ // If we have a localLink and not connected yet, try to connect
493
517
  if (this.localLink && !this.localConnected && !this.initSocket) {
494
- this.connectLocalSocket();
518
+ this.connectLocalSocket().then(() => {
519
+ this.checkProceed();
520
+ });
521
+ } else {
522
+ // If no localLink or already connected, try to proceed
523
+ this.checkProceed();
495
524
  }
496
525
  }
497
526
 
@@ -512,6 +541,27 @@ export class PreJoinPage implements OnInit {
512
541
  }
513
542
  }
514
543
 
544
+ private async checkProceed(): Promise<void> {
545
+ // If we do not need to return UI and we have noUIPreJoinOptions, proceed like in the React code
546
+ if (!this.returnUI && this.noUIPreJoinOptions) {
547
+ if ('action' in this.noUIPreJoinOptions && this.noUIPreJoinOptions.action === 'create') {
548
+ const createOptions = this.noUIPreJoinOptions as CreateMediaSFURoomOptions;
549
+ if (!createOptions.userName || !createOptions.duration || !createOptions.eventType || !createOptions.capacity) {
550
+ throw new Error('Please provide all the required parameters: userName, duration, eventType, capacity');
551
+ }
552
+ await this.handleCreateRoom();
553
+ } else if ('action' in this.noUIPreJoinOptions && this.noUIPreJoinOptions.action === 'join') {
554
+ const joinOptions = this.noUIPreJoinOptions as JoinMediaSFURoomOptions;
555
+ if (!joinOptions.userName || !joinOptions.meetingID) {
556
+ throw new Error('Please provide all the required parameters: userName, meetingID');
557
+ }
558
+ await this.handleJoinRoom();
559
+ } else {
560
+ throw new Error('Invalid options provided for creating/joining a room without UI.');
561
+ }
562
+ }
563
+ }
564
+
515
565
  toggleMode(): void {
516
566
  this.isCreateMode = !this.isCreateMode;
517
567
  this.error = '';
@@ -556,10 +606,14 @@ export class PreJoinPage implements OnInit {
556
606
 
557
607
  async roomCreator(options: { payload: any; apiUserName: string; apiKey: string; validate?: boolean }): Promise<any> {
558
608
  const { payload, apiUserName, apiKey, validate = true } = options;
559
- const response = await this.createRoomService.createRoomOnMediaSFU({
609
+ if (!this.createMediaSFURoom) {
610
+ this.createMediaSFURoom = this.createRoomService.createRoomOnMediaSFU;
611
+ }
612
+ const response = await this.createMediaSFURoom({
560
613
  payload,
561
614
  apiUserName,
562
615
  apiKey,
616
+ localLink: this.localLink,
563
617
  });
564
618
 
565
619
  if (response.success && response.data && 'roomName' in response.data) {
@@ -586,21 +640,37 @@ export class PreJoinPage implements OnInit {
586
640
 
587
641
  async handleCreateRoom(): Promise<void> {
588
642
 
589
- const { name, duration, eventType, capacity } = this.preJoinForm.value;
590
-
591
- if (!name || !duration || !eventType || !capacity) {
592
- this.error = 'Please fill all the fields.';
643
+ if (this.pending.value) {
593
644
  return;
594
645
  }
646
+ this.pending.next(true);
647
+ let payload = {} as CreateMediaSFURoomOptions;
595
648
 
596
- const payload = {
597
- action: 'create',
598
- duration: parseInt(duration),
599
- capacity: parseInt(capacity),
600
- eventType,
601
- userName: name,
602
- recordOnly: false,
603
- };
649
+ if (this.returnUI) {
650
+ const { name, duration, eventType, capacity } = this.preJoinForm.value;
651
+
652
+ if (!name || !duration || !eventType || !capacity) {
653
+ this.error = 'Please fill all the fields.';
654
+ return;
655
+ }
656
+
657
+ payload = {
658
+ action: 'create',
659
+ duration: parseInt(duration),
660
+ capacity: parseInt(capacity),
661
+ eventType,
662
+ userName: name,
663
+ recordOnly: false,
664
+ };
665
+ } else {
666
+ if (this.noUIPreJoinOptions && 'action' in this.noUIPreJoinOptions && this.noUIPreJoinOptions.action === 'create') {
667
+ payload = this.noUIPreJoinOptions as CreateMediaSFURoomOptions;
668
+ } else {
669
+ this.error = 'Invalid options provided for creating a room without UI.';
670
+ this.pending.next(false);
671
+ return;
672
+ }
673
+ }
604
674
 
605
675
  this.parameters.updateIsLoadingModalVisible(true);
606
676
 
@@ -614,13 +684,13 @@ export class PreJoinPage implements OnInit {
614
684
  Math.floor(10 + Math.random() * 99).toString();
615
685
  eventID = 'm' + eventID;
616
686
  const eventRoomParams = this.localData?.meetingRoomParams_;
617
- eventRoomParams!.type = eventType as 'chat' | 'broadcast' | 'webinar' | 'conference';
687
+ eventRoomParams!.type = payload.eventType as 'chat' | 'broadcast' | 'webinar' | 'conference';
618
688
 
619
689
  const createData: CreateLocalRoomParameters = {
620
690
  eventID: eventID,
621
- duration: parseInt(duration, 10),
622
- capacity: parseInt(capacity, 10),
623
- userName: name,
691
+ duration: payload.duration,
692
+ capacity: payload.capacity,
693
+ userName: payload.userName,
624
694
  scheduledDate: new Date(),
625
695
  secureCode: secureCode,
626
696
  waitRoom: false,
@@ -653,12 +723,14 @@ export class PreJoinPage implements OnInit {
653
723
  createData.mediasfuURL = response.data.publicURL;
654
724
  await this.createLocalRoom({ createData: createData, link: response.data.link });
655
725
  } else {
726
+ this.pending.next(false);
656
727
  this.parameters.updateIsLoadingModalVisible(false);
657
728
  this.error = 'Unable to create room on MediaSFU.';
658
729
  try {
659
730
  this.parameters.updateSocket(this.initSocket!);
660
731
  await this.createLocalRoom({ createData: createData });
661
732
  } catch (error: any) {
733
+ this.pending.next(false);
662
734
  this.parameters.updateIsLoadingModalVisible(false);
663
735
  this.error = `Unable to create room. ${error}`;
664
736
  }
@@ -668,6 +740,7 @@ export class PreJoinPage implements OnInit {
668
740
  this.parameters.updateSocket(this.initSocket!);
669
741
  await this.createLocalRoom({ createData: createData });
670
742
  } catch (error: any) {
743
+ this.pending.next(false);
671
744
  this.parameters.updateIsLoadingModalVisible(false);
672
745
  this.error = `Unable to create room. ${error}`;
673
746
  }
@@ -679,32 +752,44 @@ export class PreJoinPage implements OnInit {
679
752
  apiKey: this.credentials.apiKey,
680
753
  validate: true,
681
754
  });
755
+ this.pending.next(false);
682
756
  }
683
757
  }
684
758
 
685
759
  async handleJoinRoom(): Promise<void> {
686
- if (this.preJoinForm.invalid) {
687
- this.error = 'Please fill all the fields.';
760
+ if (this.pending.value) {
688
761
  return;
689
762
  }
763
+ this.pending.next(true);
764
+ let payload = {} as JoinMediaSFURoomOptions;
690
765
 
691
- const { name, eventID } = this.preJoinForm.value;
766
+ if (this.returnUI) {
767
+ const { name, eventID } = this.preJoinForm.value;
692
768
 
693
- if (!name || !eventID) {
694
- this.error = 'Please fill all the fields.';
695
- return;
696
- }
769
+ if (!name || !eventID) {
770
+ this.error = 'Please fill all the fields.';
771
+ return;
772
+ }
697
773
 
698
- const payload = {
699
- action: 'join',
700
- meetingID: eventID,
701
- userName: name,
702
- };
774
+ payload = {
775
+ action: 'join',
776
+ meetingID: eventID,
777
+ userName: name,
778
+ };
779
+ } else {
780
+ if (this.noUIPreJoinOptions && 'action' in this.noUIPreJoinOptions && this.noUIPreJoinOptions.action === 'join') {
781
+ payload = this.noUIPreJoinOptions as JoinMediaSFURoomOptions;
782
+ } else {
783
+ this.error = 'Invalid options provided for joining a room without UI.';
784
+ this.pending.next(false);
785
+ return;
786
+ }
787
+ }
703
788
 
704
789
  if (this.localLink && !this.localLink.includes('mediasfu.com')) {
705
- const joinData = {
706
- eventID: eventID,
707
- userName: name,
790
+ const joinData: JoinLocalEventRoomParameters = {
791
+ eventID: payload.meetingID,
792
+ userName: payload.userName,
708
793
  secureCode: '',
709
794
  videoPreference: null,
710
795
  audioPreference: null,
@@ -712,15 +797,20 @@ export class PreJoinPage implements OnInit {
712
797
  };
713
798
 
714
799
  await this.joinLocalRoom({ joinData: joinData });
800
+ this.pending.next(false);
715
801
  return;
716
802
  }
717
803
 
718
804
  this.parameters.updateIsLoadingModalVisible(true);
719
805
  try {
720
- const response = await this.joinRoomService.joinRoomOnMediaSFU({
806
+ if (!this.joinMediaSFURoom) {
807
+ this.joinMediaSFURoom = this.joinRoomService.joinRoomOnMediaSFU;
808
+ }
809
+ const response = await this.joinMediaSFURoom({
721
810
  payload,
722
811
  apiUserName: this.credentials.apiUserName,
723
812
  apiKey: this.credentials.apiKey,
813
+ localLink: this.localLink,
724
814
  });
725
815
 
726
816
  if (response.success && response.data && 'roomName' in response.data) {
@@ -728,13 +818,15 @@ export class PreJoinPage implements OnInit {
728
818
  apiUserName: response.data.roomName,
729
819
  apiToken: response.data.secret,
730
820
  link: response.data.link,
731
- userName: name,
821
+ userName: payload.userName,
732
822
  parameters: this.parameters,
733
823
  validate: true,
734
824
  });
735
825
  this.error = '';
826
+ this.pending.next(false);
736
827
  } else {
737
828
  this.parameters.updateIsLoadingModalVisible(false);
829
+ this.pending.next(false);
738
830
  this.error = `Unable to connect to room. ${
739
831
  response.data ? ('error' in response.data ? response.data.error : '') : ''
740
832
  }`;
@@ -745,7 +837,6 @@ export class PreJoinPage implements OnInit {
745
837
  }
746
838
  }
747
839
  }
748
-
749
840
  ```
750
841
 
751
842
  ### IP Blockage Warning And Local UI Development
@@ -760,8 +851,7 @@ In this mode, the module will operate locally without making requests to MediaSF
760
851
 
761
852
  ### Example for Generic UI to Render Broadcast Room
762
853
 
763
- ```javascript
764
-
854
+ ```typescript
765
855
  import { Component, OnInit } from '@angular/core';
766
856
  import {
767
857
  MediasfuBroadcast,
@@ -866,7 +956,7 @@ export class AppComponent implements OnInit {
866
956
 
867
957
  ### Example for Generic View
868
958
 
869
- ```javascript
959
+ ```typescript
870
960
  import { Component, OnInit } from '@angular/core';
871
961
  import {
872
962
  GenerateRandomParticipants,
@@ -881,44 +971,41 @@ import {
881
971
  PreJoinPage,
882
972
  } from 'mediasfu-angular';
883
973
 
974
+ // Assume all missing imports are similar to the previous example
975
+
884
976
 
885
977
  /**
886
- * The main application component for MediaSFU.
887
- *
888
- * This component initializes the necessary configuration and credentials for the MediaSFU application.
889
- * Users can specify their own Community Edition (CE) server, utilize MediaSFU Cloud by default, or enable MediaSFU Cloud for egress features.
978
+ * AppComponent
890
979
  *
891
- * @remarks
892
- * - **Using Your Own Community Edition (CE) Server**: Set the `localLink` to point to your CE server.
893
- * - **Using MediaSFU Cloud by Default**: If not using a custom server (`localLink` is empty), the application connects to MediaSFU Cloud.
894
- * - **MediaSFU Cloud Egress Features**: To enable cloud recording, capturing, and returning real-time images and audio buffers,
895
- * set `connectMediaSFU` to `true` in addition to specifying your `localLink`.
896
- * - **Credentials Requirement**: If not using your own server, provide `apiUserName` and `apiKey`. The same applies when using MediaSFU Cloud for egress.
897
- * - **Deprecated Feature**: `useLocalUIMode` is deprecated due to updates for strong typing and improved configuration options.
980
+ * This component demonstrates how to:
981
+ * - Configure credentials for MediaSFU Cloud and/or Community Edition (CE).
982
+ * - Use MediaSFU with or without a custom server.
983
+ * - Integrate a pre-join page.
984
+ * - Disable the default MediaSFU UI and manage state through `sourceParameters` for a fully custom frontend.
898
985
  *
899
- * @component
900
- * @example
901
- * ```typescript
902
- * // Example usage of the AppComponent
903
- * @NgModule({
904
- * declarations: [AppComponent],
905
- * imports: [BrowserModule, MediasfuWebinar],
906
- * bootstrap: [AppComponent]
907
- * })
908
- * export class AppModule { }
909
- * ```
986
+ * Basic instructions:
987
+ * 1. Set `localLink` to your CE server if you have one, or leave it blank to use MediaSFU Cloud.
988
+ * 2. Set `connectMediaSFU` to determine whether you're connecting to MediaSFU Cloud services.
989
+ * 3. Provide credentials if using MediaSFU Cloud (dummy credentials are acceptable in certain scenarios).
990
+ * 4. If you prefer a custom UI, set `returnUI` to false and handle all interactions via `sourceParameters` and `updateSourceParameters`.
991
+ * 5. For secure production usage, use custom `createMediaSFURoom` and `joinMediaSFURoom` functions to forward requests through your backend.
910
992
  */
911
993
  @Component({
912
994
  selector: 'app-root',
913
- imports: [MediasfuWebinar],
995
+ imports: [MediasfuGeneric],
914
996
  template: `
915
- <app-mediasfu-webinar
916
- [credentials]="credentials"
917
- [localLink]="localLink"
918
- [connectMediaSFU]="connectMediaSFU"
919
- [PrejoinPage]="PreJoinPage"
920
- [seedData]="seedData">
921
- </app-mediasfu-webinar>
997
+ <app-mediasfu-generic
998
+ [PrejoinPage]="PreJoinPage"
999
+ [credentials]="credentials"
1000
+ [localLink]="localLink"
1001
+ [connectMediaSFU]="connectMediaSFU"
1002
+ [returnUI]="returnUI"
1003
+ [noUIPreJoinOptions]="!returnUI ? noUIPreJoinOptions : undefined"
1004
+ [sourceParameters]="!returnUI ? sourceParameters : undefined"
1005
+ [updateSourceParameters]="!returnUI ? updateSourceParameters : undefined"
1006
+ [createMediaSFURoom]="createRoomOnMediaSFU.createRoomOnMediaSFU"
1007
+ [joinMediaSFURoom]="joinRoomOnMediaSFU.joinRoomOnMediaSFU">
1008
+ </app-mediasfu-generic>
922
1009
  `,
923
1010
  providers: [
924
1011
  GenerateRandomParticipants,
@@ -928,54 +1015,187 @@ import {
928
1015
  ],
929
1016
  })
930
1017
  export class AppComponent implements OnInit {
931
- // ========================
932
- // ====== CONFIGURATION ======
933
- // ========================
1018
+ // =========================================================
1019
+ // API CREDENTIALS CONFIGURATION
1020
+ // =========================================================
1021
+ //
1022
+ // Scenario A: Not using MediaSFU Cloud at all.
1023
+ // - No credentials needed. Just set localLink to your CE server.
1024
+ // Example:
1025
+ /*
1026
+ credentials = {};
1027
+ localLink = 'http://your-ce-server.com'; // e.g., 'http://localhost:3000'
1028
+ connectMediaSFU = localLink.trim() !== '';
1029
+ */
934
1030
 
935
- /**
936
- * Mediasfu account credentials.
937
- * Replace 'your_api_username' and 'your_api_key' with your actual credentials.
938
- * Not needed if using a custom server without MediaSFU Cloud Egress features.
939
- */
1031
+ // Scenario B: Using MediaSFU CE + MediaSFU Cloud for Egress only.
1032
+ // - Use dummy credentials (8 characters for userName, 64 characters for apiKey).
1033
+ // - Your CE backend will forward requests with your real credentials.
1034
+ /*
940
1035
  credentials = {
941
- apiUserName: 'your_api_username',
942
- apiKey: 'your_api_key',
1036
+ apiUserName: 'dummyUsr', // 8 characters
1037
+ apiKey: '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', // 64 characters
943
1038
  };
1039
+ localLink = 'http://your-ce-server.com'; // e.g., 'http://localhost:3000'
1040
+ connectMediaSFU = localLink.trim() !== '';
1041
+ */
944
1042
 
945
- /**
946
- * Specify your Community Edition (CE) server link.
947
- * Leave as an empty string if not using a custom server.
948
- */
949
- localLink = 'http://localhost:3000'; // Set to '' if not using your own server
1043
+ // Scenario C: Using MediaSFU Cloud without your own server.
1044
+ // - For development, use your actual or dummy credentials.
1045
+ // - In production, securely handle credentials server-side and use custom room functions.
1046
+ credentials = {
1047
+ apiUserName: 'yourDevUser', // 8 characters recommended for dummy
1048
+ apiKey: 'yourDevApiKey1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', // 64 characters
1049
+ };
1050
+ localLink = ''; // Leave empty if not using your own server
1051
+ connectMediaSFU = true; // Set to true if using MediaSFU Cloud since localLink is empty
1052
+
1053
+ // =========================================================
1054
+ // UI RENDERING OPTIONS
1055
+ // =========================================================
1056
+ //
1057
+ // If you want a fully custom UI (e.g., a custom layout inspired by WhatsApp):
1058
+ // 1. Set `returnUI = false` to prevent the default MediaSFU UI from rendering.
1059
+ // 2. Provide `noUIPreJoinOptions` to simulate what would have been entered on a pre-join page.
1060
+ // 3. Use `sourceParameters` and `updateSourceParameters` to access and update state/actions.
1061
+ // 4. No need for any of the above if you're using the default MediaSFU UI.
1062
+ //
1063
+ // Example noUIPreJoinOptions for creating a room:
1064
+ noUIPreJoinOptions: CreateMediaSFURoomOptions | JoinMediaSFURoomOptions = {
1065
+ action: 'create',
1066
+ capacity: 10,
1067
+ duration: 15,
1068
+ eventType: 'broadcast',
1069
+ userName: 'Prince',
1070
+ };
1071
+
1072
+ // Example noUIPreJoinOptions for joining a room:
1073
+ // noUIPreJoinOptions: CreateMediaSFURoomOptions | JoinMediaSFURoomOptions = {
1074
+ // action: 'join',
1075
+ // userName: 'Prince',
1076
+ // meetingID: 'yourMeetingID'
1077
+ // };
1078
+
1079
+ returnUI = true; // Set to false for custom UI, true for default MediaSFU UI
1080
+
1081
+ sourceParameters: { [key: string]: any } = {};
950
1082
 
951
1083
  /**
952
- * Automatically set `connectMediaSFU` to `true` if `localLink` is provided,
953
- * indicating the use of MediaSFU Cloud by default.
1084
+ * Function to update sourceParameters state.
954
1085
  *
955
- * - If `localLink` is not empty, MediaSFU Cloud will be used for additional features.
956
- * - If `localLink` is empty, the application will connect to MediaSFU Cloud by default.
1086
+ * @param data - The data to update sourceParameters with.
957
1087
  */
958
- connectMediaSFU = this.localLink.trim() !== ''; // set to false if not using MediaSFU Cloud for Main Server or Egress
959
-
960
- // ========================
961
- // ====== USE CASES ======
962
- // ========================
1088
+ updateSourceParameters = (data: { [key: string]: any }) => {
1089
+ this.sourceParameters = data;
1090
+ };
963
1091
 
964
- // Deprecated Feature: useLocalUIMode
965
- // This feature is deprecated due to updates for strong typing.
966
- // It is no longer required and should not be used in new implementations.
1092
+ // =========================================================
1093
+ // CUSTOM ROOM FUNCTIONS (OPTIONAL)
1094
+ // =========================================================
1095
+ //
1096
+ // To securely forward requests to MediaSFU:
1097
+ // - Implement custom `createMediaSFURoom` and `joinMediaSFURoom` functions.
1098
+ // - These functions send requests to your server, which then communicates with MediaSFU Cloud.
1099
+ //
1100
+ // The imported `createRoomOnMediaSFU` and `joinRoomOnMediaSFU` are examples.
1101
+ //
1102
+ // If using MediaSFU CE backend, ensure your server endpoints:
1103
+ // - Validate dummy credentials.
1104
+ // - Forward requests to mediasfu.com with real credentials.
1105
+
1106
+ // =========================================================
1107
+ // COMPONENT SELECTION AND RENDERING
1108
+ // =========================================================
1109
+ //
1110
+ // Multiple components are available depending on your event type:
1111
+ // MediasfuBroadcast, MediasfuChat, MediasfuWebinar, MediasfuConference
1112
+ //
1113
+ // By default, we'll use MediasfuGeneric with custom settings.
1114
+ //
1115
+ // Uncomment the desired component below and comment out the others as needed.
1116
+ //
1117
+ // Example of MediaSFU CE with no MediaSFU Cloud
1118
+ // return (
1119
+ // <app-mediasfu-generic
1120
+ // [PrejoinPage]="PreJoinPage"
1121
+ // [localLink]="localLink">
1122
+ // </app-mediasfu-generic>
1123
+ // );
1124
+
1125
+ // Example of MediaSFU CE + MediaSFU Cloud for Egress only
1126
+ // return (
1127
+ // <app-mediasfu-generic
1128
+ // [PrejoinPage]="PreJoinPage"
1129
+ // [credentials]="credentials"
1130
+ // [localLink]="localLink"
1131
+ // [connectMediaSFU]="connectMediaSFU">
1132
+ // </app-mediasfu-generic>
1133
+ // );
1134
+
1135
+ // Example of MediaSFU Cloud only
1136
+ // return (
1137
+ // <app-mediasfu-generic
1138
+ // [PrejoinPage]="PreJoinPage"
1139
+ // [credentials]="credentials"
1140
+ // [connectMediaSFU]="connectMediaSFU">
1141
+ // </app-mediasfu-generic>
1142
+ // );
1143
+
1144
+ // Example of MediaSFU CE + MediaSFU Cloud for Egress only with custom UI
1145
+ // return (
1146
+ // <app-mediasfu-generic
1147
+ // [PrejoinPage]="PreJoinPage"
1148
+ // [credentials]="credentials"
1149
+ // [localLink]="localLink"
1150
+ // [connectMediaSFU]="connectMediaSFU"
1151
+ // [returnUI]="false"
1152
+ // [noUIPreJoinOptions]="noUIPreJoinOptions"
1153
+ // [sourceParameters]="sourceParameters"
1154
+ // [updateSourceParameters]="updateSourceParameters"
1155
+ // [createMediaSFURoom]="createRoomOnMediaSFU"
1156
+ // [joinMediaSFURoom]="joinRoomOnMediaSFU">
1157
+ // </app-mediasfu-generic>
1158
+ // );
1159
+
1160
+ // Example of MediaSFU Cloud only with custom UI
1161
+ // return (
1162
+ // <app-mediasfu-generic
1163
+ // [PrejoinPage]="PreJoinPage"
1164
+ // [credentials]="credentials"
1165
+ // [connectMediaSFU]="connectMediaSFU"
1166
+ // [returnUI]="false"
1167
+ // [noUIPreJoinOptions]="noUIPreJoinOptions"
1168
+ // [sourceParameters]="sourceParameters"
1169
+ // [updateSourceParameters]="updateSourceParameters"
1170
+ // [createMediaSFURoom]="createRoomOnMediaSFU"
1171
+ // [joinMediaSFURoom]="joinRoomOnMediaSFU">
1172
+ // </app-mediasfu-generic>
1173
+ // );
1174
+
1175
+ // Example of using MediaSFU CE only with custom UI
1176
+ // return (
1177
+ // <app-mediasfu-generic
1178
+ // [PrejoinPage]="PreJoinPage"
1179
+ // [localLink]="localLink"
1180
+ // [connectMediaSFU]="false"
1181
+ // [returnUI]="false"
1182
+ // [noUIPreJoinOptions]="noUIPreJoinOptions"
1183
+ // [sourceParameters]="sourceParameters"
1184
+ // [updateSourceParameters]="updateSourceParameters">
1185
+ // </app-mediasfu-generic>
1186
+ // );
967
1187
 
968
1188
  /**
969
- * Uncomment and configure the following section if you intend to use seed data
970
- * for generating random participants and messages.
1189
+ * Default Rendering: MediasfuGeneric with Updated Configuration
971
1190
  *
972
- * Note: This is deprecated and maintained only for legacy purposes.
1191
+ * Renders the MediasfuGeneric component with specified server and cloud connection settings.
1192
+ * Configured for custom UI usage if `returnUI` is set to false.
973
1193
  */
974
- /*
975
- useSeed = false;
976
- seedData: any = {};
977
-
978
1194
  ngOnInit(): void {
1195
+ // Deprecated Feature: useSeed and seedData for generating random participants and messages
1196
+ // Uncomment and configure the following section if you intend to use seed data
1197
+
1198
+ /*
979
1199
  if (this.useSeed) {
980
1200
  const memberName = 'Alice';
981
1201
  const hostName = 'Fred';
@@ -1021,8 +1241,8 @@ export class AppComponent implements OnInit {
1021
1241
 
1022
1242
  // Determine whether to use local UI mode
1023
1243
  this.useLocalUIMode = this.useSeed;
1244
+ */
1024
1245
  }
1025
- */
1026
1246
 
1027
1247
  // ========================
1028
1248
  // ====== COMPONENT SELECTION ======
@@ -1099,7 +1319,6 @@ export class AppComponent implements OnInit {
1099
1319
  * Renders the MediasfuWebinar component with specified server and cloud connection settings.
1100
1320
  * This is the default use case if no specific event type is selected.
1101
1321
  */
1102
- seedData: any = {}; // Initialize seedData as empty object
1103
1322
 
1104
1323
  // Reference to the PreJoinPage component
1105
1324
  PreJoinPage = PreJoinPage;
@@ -1108,7 +1327,9 @@ export class AppComponent implements OnInit {
1108
1327
  private generateRandomParticipants: GenerateRandomParticipants,
1109
1328
  private generateRandomMessages: GenerateRandomMessages,
1110
1329
  private generateRandomRequestList: GenerateRandomRequestList,
1111
- private generateRandomWaitingRoomList: GenerateRandomWaitingRoomList
1330
+ private generateRandomWaitingRoomList: GenerateRandomWaitingRoomList,
1331
+ public createRoomOnMediaSFU: CreateRoomOnMediaSFU,
1332
+ public joinRoomOnMediaSFU: JoinRoomOnMediaSFU
1112
1333
  ) { }
1113
1334
 
1114
1335
  // Deprecated Feature: useSeed and seedData for generating random participants and messages
@@ -1118,59 +1339,203 @@ export class AppComponent implements OnInit {
1118
1339
  // eventType = 'webinar';
1119
1340
  // useLocalUIMode = false;
1120
1341
 
1121
- ngOnInit(): void {
1122
- // If using seed data, generate random participants and messages - DEPRECATED FEATURE
1123
- // Note: This feature is deprecated and maintained only for legacy purposes.
1124
- // Uncomment and configure the following section if you intend to use seed data
1125
-
1126
- // if (this.useSeed) {
1127
- // const memberName = 'Alice';
1128
- // const hostName = 'Fred';
1129
-
1130
- // // Generate random participants
1131
- // const participants_ = this.generateRandomParticipants.generateRandomParticipants({
1132
- // member: memberName,
1133
- // coHost: '',
1134
- // host: hostName,
1135
- // forChatBroadcast: this.eventType === 'broadcast' || this.eventType === 'chat',
1136
- // });
1137
-
1138
- // // Generate random messages
1139
- // const messages_ = this.generateRandomMessages.generateRandomMessages({
1140
- // participants: participants_,
1141
- // member: memberName,
1142
- // host: hostName,
1143
- // forChatBroadcast: this.eventType === 'broadcast' || this.eventType === 'chat',
1144
- // });
1145
-
1146
- // // Generate random request list
1147
- // const requests_ = this.generateRandomRequestList.generateRandomRequestList({
1148
- // participants: participants_,
1149
- // hostName: memberName,
1150
- // coHostName: '',
1151
- // numberOfRequests: 3,
1152
- // });
1153
-
1154
- // // Generate random waiting room list
1155
- // const waitingList_ = this.generateRandomWaitingRoomList.generateRandomWaitingRoomList();
1156
-
1157
- // // Assign generated data to seedData
1158
- // this.seedData = {
1159
- // participants: participants_,
1160
- // messages: messages_,
1161
- // requests: requests_,
1162
- // waitingList: waitingList_,
1163
- // member: memberName,
1164
- // host: hostName,
1165
- // eventType: this.eventType,
1166
- // };
1167
- // }
1168
-
1169
- // Determine whether to use local UI mode, deprecated feature
1170
- // this.useLocalUIMode = this.useSeed;
1171
- }
1172
1342
  }
1173
1343
 
1344
+ export default AppComponent;
1345
+
1346
+ /**
1347
+ * =========================================================
1348
+ * ADDITIONAL NOTES
1349
+ * =========================================================
1350
+ *
1351
+ * Handling Core Methods:
1352
+ * Once `sourceParameters` is populated, you can call core methods like `clickVideo` or `clickAudio` directly:
1353
+ * Example:
1354
+ * sourceParameters.clickVideo({ ...sourceParameters });
1355
+ * sourceParameters.clickAudio({ ...sourceParameters });
1356
+ *
1357
+ * This allows your custom UI to directly interact with MediaSFU functionalities.
1358
+ *
1359
+ * Deprecated Features (Seed Data):
1360
+ * The seed data generation feature is deprecated. Avoid using `useLocalUIMode` or `useSeed` in new implementations.
1361
+ *
1362
+ * Security Considerations:
1363
+ * - **Protect API Credentials:** Ensure that API credentials are not exposed in the frontend. Use environment variables and secure backend services to handle sensitive information.
1364
+ * - **Use HTTPS:** Always use HTTPS to secure data transmission between the client and server.
1365
+ * - **Validate Inputs:** Implement proper validation and error handling on both client and server sides to prevent malicious inputs.
1366
+ *
1367
+ * Example CE Backend Setup:
1368
+ * If using MediaSFU CE + MediaSFU Cloud, your backend might look like this:
1369
+ *
1370
+ * ```javascript
1371
+ * // Endpoint for `createRoom`
1372
+ * app.post("/createRoom", async (req, res) => {
1373
+ * try {
1374
+ * const payload = req.body;
1375
+ * const [apiUserName, apiKey] = req.headers.authorization
1376
+ * .replace("Bearer ", "")
1377
+ * .split(":");
1378
+ *
1379
+ * // Verify temporary credentials
1380
+ * if (!apiUserName || !apiKey || !verifyCredentials(apiUserName, apiKey)) {
1381
+ * return res.status(401).json({ error: "Invalid or expired credentials" });
1382
+ * }
1383
+ *
1384
+ * const response = await fetch("https://mediasfu.com/v1/rooms/", {
1385
+ * method: "POST",
1386
+ * headers: {
1387
+ * "Content-Type": "application/json",
1388
+ * Authorization: `Bearer ${actualApiUserName}:${actualApiKey}`,
1389
+ * },
1390
+ * body: JSON.stringify(payload),
1391
+ * });
1392
+ *
1393
+ * const result = await response.json();
1394
+ * res.status(response.status).json(result);
1395
+ * } catch (error) {
1396
+ * console.error("Error creating room:", error);
1397
+ * res.status(500).json({ error: "Internal server error" });
1398
+ * }
1399
+ * });
1400
+ *
1401
+ * // Endpoint for `joinRoom`
1402
+ * app.post("/joinRoom", async (req, res) => {
1403
+ * try {
1404
+ * const payload = req.body;
1405
+ * const [apiUserName, apiKey] = req.headers.authorization
1406
+ * .replace("Bearer ", "")
1407
+ * .split(":");
1408
+ *
1409
+ * // Verify temporary credentials
1410
+ * if (!apiUserName || !apiKey || !verifyCredentials(apiUserName, apiKey)) {
1411
+ * return res.status(401).json({ error: "Invalid or expired credentials" });
1412
+ * }
1413
+ *
1414
+ * const response = await fetch("https://mediasfu.com/v1/rooms", {
1415
+ * method: "POST",
1416
+ * headers: {
1417
+ * "Content-Type": "application/json",
1418
+ * Authorization: `Bearer ${actualApiUserName}:${actualApiKey}`,
1419
+ * },
1420
+ * body: JSON.stringify(payload),
1421
+ * });
1422
+ *
1423
+ * const result = await response.json();
1424
+ * res.status(response.status).json(result);
1425
+ * } catch (error) {
1426
+ * console.error("Error joining room:", error);
1427
+ * res.status(500).json({ error: "Internal server error" });
1428
+ * }
1429
+ * });
1430
+ * ```
1431
+ *
1432
+ * Custom Room Function Implementation:
1433
+ * Below are examples of how to implement custom functions for creating and joining rooms securely:
1434
+ *
1435
+ * ```typescript
1436
+ * import { CreateJoinRoomError, CreateJoinRoomResponse, CreateJoinRoomType, CreateMediaSFURoomOptions, JoinMediaSFURoomOptions } from '../../@types/types';
1437
+ *
1438
+ *
1439
+ * * Async function to create a room on MediaSFU.
1440
+ * *
1441
+ * * @param {object} options - The options for creating a room.
1442
+ * * @param {CreateMediaSFURoomOptions} options.payload - The payload for the API request.
1443
+ * * @param {string} options.apiUserName - The API username.
1444
+ * * @param {string} options.apiKey - The API key.
1445
+ * * @param {string} options.localLink - The local link.
1446
+ * * @returns {Promise<{ data: CreateJoinRoomResponse | CreateJoinRoomError | null; success: boolean; }>} The response from the API.
1447
+ *
1448
+ * export const createRoomOnMediaSFU: CreateJoinRoomType = async ({
1449
+ * payload,
1450
+ * apiUserName,
1451
+ * apiKey,
1452
+ * localLink = '',
1453
+ * }) => {
1454
+ * try {
1455
+ * let finalLink = 'https://mediasfu.com/v1/rooms/';
1456
+ *
1457
+ * // Update finalLink if using a local server
1458
+ * if (localLink) {
1459
+ * finalLink = `${localLink}/createRoom`;
1460
+ * }
1461
+ *
1462
+ * const response = await fetch(finalLink, {
1463
+ * method: 'POST',
1464
+ * headers: {
1465
+ * 'Content-Type': 'application/json',
1466
+ * Authorization: `Bearer ${apiUserName}:${apiKey}`,
1467
+ * },
1468
+ * body: JSON.stringify(payload),
1469
+ * });
1470
+ *
1471
+ * if (!response.ok) {
1472
+ * throw new Error(`HTTP error! Status: ${response.status}`);
1473
+ * }
1474
+ *
1475
+ * const data: CreateJoinRoomResponse = await response.json();
1476
+ * return { data, success: true };
1477
+ * } catch (error) {
1478
+ * const errorMessage = (error as Error).message || 'unknown error';
1479
+ * return {
1480
+ * data: { error: `Unable to create room, ${errorMessage}` },
1481
+ * success: false,
1482
+ * };
1483
+ * }
1484
+ * };
1485
+ *
1486
+ *
1487
+ * * Async function to join a room on MediaSFU.
1488
+ * *
1489
+ * * @param {object} options - The options for joining a room.
1490
+ * * @param {JoinMediaSFURoomOptions} options.payload - The payload for the API request.
1491
+ * * @param {string} options.apiUserName - The API username.
1492
+ * * @param {string} options.apiKey - The API key.
1493
+ * * @param {string} options.localLink - The local link.
1494
+ * * @returns {Promise<{ data: CreateJoinRoomResponse | CreateJoinRoomError | null; success: boolean; }>} The response from the API.
1495
+ *
1496
+ * export const joinRoomOnMediaSFU: JoinRoomOnMediaSFUType = async ({
1497
+ * payload,
1498
+ * apiUserName,
1499
+ * apiKey,
1500
+ * localLink = '',
1501
+ * }) => {
1502
+ * try {
1503
+ * let finalLink = 'https://mediasfu.com/v1/rooms/join';
1504
+ *
1505
+ * // Update finalLink if using a local server
1506
+ * if (localLink) {
1507
+ * finalLink = `${localLink}/joinRoom`;
1508
+ * }
1509
+ *
1510
+ * const response = await fetch(finalLink, {
1511
+ * method: 'POST',
1512
+ * headers: {
1513
+ * 'Content-Type': 'application/json',
1514
+ * Authorization: `Bearer ${apiUserName}:${apiKey}`,
1515
+ * },
1516
+ * body: JSON.stringify(payload),
1517
+ * });
1518
+ *
1519
+ * if (!response.ok) {
1520
+ * throw new Error(`HTTP error! Status: ${response.status}`);
1521
+ * }
1522
+ *
1523
+ * const data: CreateJoinRoomResponse = await response.json();
1524
+ * return { data, success: true };
1525
+ * } catch (error) {
1526
+ * const errorMessage = (error as Error).message || 'unknown error';
1527
+ * return {
1528
+ * data: { error: `Unable to join room, ${errorMessage}` },
1529
+ * success: false,
1530
+ * };
1531
+ * }
1532
+ * };
1533
+ * ```
1534
+ *
1535
+ * =======================
1536
+ * ====== END OF GUIDE ======
1537
+ * =======================
1538
+ */
1174
1539
 
1175
1540
  ```
1176
1541