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/dist/README.md CHANGED
@@ -34,8 +34,11 @@ MediaSFU offers a cutting-edge streaming experience that empowers users to custo
34
34
  # MediaSFU Angular Module Documentation
35
35
 
36
36
  ## Unlock the Power of MediaSFU Community Edition
37
+
37
38
  **MediaSFU Community Edition is free and open-source**—perfect for developers who want to run their own media server without upfront costs. With robust features and simple setup, you can launch your media solution in minutes. **Ready to scale?** Upgrade seamlessly to **MediaSFU Cloud** for enterprise-grade performance and global scalability.
39
+
38
40
  **[Get started now on GitHub!](https://github.com/MediaSFU/MediaSFUOpen)**
41
+
39
42
  ---
40
43
 
41
44
  ## Table of Contents
@@ -285,19 +288,21 @@ updateValidated(true);
285
288
 
286
289
  See the following code for the PreJoinPage page logic:
287
290
 
288
- ```javascript
289
- import { Component, Inject, Input, OnInit, Optional } from '@angular/core';
291
+ ```typescript
292
+ import { Component, Inject, Input, OnInit, Optional } from '@angular/core';
290
293
  import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
291
294
  import { CommonModule } from '@angular/common';
292
295
  import { Socket } from 'socket.io-client';
293
296
  import {
294
297
  ConnectSocketType, ShowAlert,
295
298
  ConnectLocalSocketType, ResponseLocalConnection,
296
- ResponseLocalConnectionData, RecordingParams, MeetingRoomParams
299
+ ResponseLocalConnectionData, RecordingParams, MeetingRoomParams,
300
+ CreateMediaSFURoomOptions,JoinMediaSFURoomOptions,
297
301
  } from '../../../@types/types';
298
302
  import { CheckLimitsAndMakeRequest } from '../../../methods/utils/check-limits-and-make-request.service';
299
303
  import { CreateRoomOnMediaSFU } from '../../../methods/utils/create-room-on-media-sfu.service';
300
- 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';
301
306
 
302
307
  export interface JoinLocalEventRoomParameters {
303
308
  eventID: string;
@@ -367,6 +372,10 @@ export interface PreJoinPageOptions {
367
372
  connectMediaSFU?: boolean;
368
373
  parameters: PreJoinPageParameters;
369
374
  credentials?: Credentials;
375
+ returnUI?: boolean;
376
+ noUIPreJoinOptions?: CreateMediaSFURoomOptions | JoinMediaSFURoomOptions;
377
+ createMediaSFURoom?: CreateRoomOnMediaSFUType;
378
+ joinMediaSFURoom?: JoinRoomOnMediaSFUType;
370
379
  }
371
380
 
372
381
  export type PreJoinPageType = (options: PreJoinPageOptions) => HTMLElement;
@@ -451,6 +460,11 @@ export class PreJoinPage implements OnInit {
451
460
  @Input() credentials: Credentials = { apiUserName: 'yourAPIUSERNAME', apiKey: 'yourAPIKEY' };
452
461
  @Input() localLink: string | undefined = "";
453
462
  @Input() connectMediaSFU: boolean | undefined = true;
463
+ @Input() returnUI?: boolean;
464
+ @Input() noUIPreJoinOptions?: CreateMediaSFURoomOptions | JoinMediaSFURoomOptions;
465
+ @Input() createMediaSFURoom?: CreateRoomOnMediaSFUType;
466
+ @Input() joinMediaSFURoom?: JoinRoomOnMediaSFUType;
467
+
454
468
 
455
469
  isCreateMode = false;
456
470
  preJoinForm: FormGroup;
@@ -462,15 +476,23 @@ export class PreJoinPage implements OnInit {
462
476
  localData: ResponseLocalConnectionData | undefined = undefined;
463
477
  initSocket: Socket | undefined = undefined;
464
478
 
479
+ pending = new BehaviorSubject<boolean>(false);
480
+
465
481
  constructor(
466
482
  private fb: FormBuilder,
467
483
  @Optional() @Inject('parameters') injectedParameters: PreJoinPageParameters,
468
484
  @Optional() @Inject('credentials') injectedCredentials: Credentials,
469
485
  @Optional() @Inject('localLink') injectedLocalLink: string,
470
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
+
471
493
  private checkLimitsService: CheckLimitsAndMakeRequest,
472
494
  private createRoomService: CreateRoomOnMediaSFU,
473
- private joinRoomService: JoinRoomOnMediaSFUService
495
+ private joinRoomService: JoinRoomOnMediaSFU
474
496
  ) {
475
497
  this.preJoinForm = this.fb.group({
476
498
  name: ['', Validators.required],
@@ -483,12 +505,22 @@ export class PreJoinPage implements OnInit {
483
505
  this.credentials = injectedCredentials || this.credentials;
484
506
  this.localLink = injectedLocalLink || this.localLink;
485
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;
486
512
 
487
513
  }
488
514
 
489
515
  ngOnInit(): void {
516
+ // If we have a localLink and not connected yet, try to connect
490
517
  if (this.localLink && !this.localConnected && !this.initSocket) {
491
- 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();
492
524
  }
493
525
  }
494
526
 
@@ -509,6 +541,27 @@ export class PreJoinPage implements OnInit {
509
541
  }
510
542
  }
511
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
+
512
565
  toggleMode(): void {
513
566
  this.isCreateMode = !this.isCreateMode;
514
567
  this.error = '';
@@ -553,10 +606,14 @@ export class PreJoinPage implements OnInit {
553
606
 
554
607
  async roomCreator(options: { payload: any; apiUserName: string; apiKey: string; validate?: boolean }): Promise<any> {
555
608
  const { payload, apiUserName, apiKey, validate = true } = options;
556
- const response = await this.createRoomService.createRoomOnMediaSFU({
609
+ if (!this.createMediaSFURoom) {
610
+ this.createMediaSFURoom = this.createRoomService.createRoomOnMediaSFU;
611
+ }
612
+ const response = await this.createMediaSFURoom({
557
613
  payload,
558
614
  apiUserName,
559
615
  apiKey,
616
+ localLink: this.localLink,
560
617
  });
561
618
 
562
619
  if (response.success && response.data && 'roomName' in response.data) {
@@ -583,21 +640,37 @@ export class PreJoinPage implements OnInit {
583
640
 
584
641
  async handleCreateRoom(): Promise<void> {
585
642
 
586
- const { name, duration, eventType, capacity } = this.preJoinForm.value;
587
-
588
- if (!name || !duration || !eventType || !capacity) {
589
- this.error = 'Please fill all the fields.';
643
+ if (this.pending.value) {
590
644
  return;
591
645
  }
646
+ this.pending.next(true);
647
+ let payload = {} as CreateMediaSFURoomOptions;
592
648
 
593
- const payload = {
594
- action: 'create',
595
- duration: parseInt(duration),
596
- capacity: parseInt(capacity),
597
- eventType,
598
- userName: name,
599
- recordOnly: false,
600
- };
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
+ }
601
674
 
602
675
  this.parameters.updateIsLoadingModalVisible(true);
603
676
 
@@ -611,13 +684,13 @@ export class PreJoinPage implements OnInit {
611
684
  Math.floor(10 + Math.random() * 99).toString();
612
685
  eventID = 'm' + eventID;
613
686
  const eventRoomParams = this.localData?.meetingRoomParams_;
614
- eventRoomParams!.type = eventType as 'chat' | 'broadcast' | 'webinar' | 'conference';
687
+ eventRoomParams!.type = payload.eventType as 'chat' | 'broadcast' | 'webinar' | 'conference';
615
688
 
616
689
  const createData: CreateLocalRoomParameters = {
617
690
  eventID: eventID,
618
- duration: parseInt(duration, 10),
619
- capacity: parseInt(capacity, 10),
620
- userName: name,
691
+ duration: payload.duration,
692
+ capacity: payload.capacity,
693
+ userName: payload.userName,
621
694
  scheduledDate: new Date(),
622
695
  secureCode: secureCode,
623
696
  waitRoom: false,
@@ -650,12 +723,14 @@ export class PreJoinPage implements OnInit {
650
723
  createData.mediasfuURL = response.data.publicURL;
651
724
  await this.createLocalRoom({ createData: createData, link: response.data.link });
652
725
  } else {
726
+ this.pending.next(false);
653
727
  this.parameters.updateIsLoadingModalVisible(false);
654
728
  this.error = 'Unable to create room on MediaSFU.';
655
729
  try {
656
730
  this.parameters.updateSocket(this.initSocket!);
657
731
  await this.createLocalRoom({ createData: createData });
658
732
  } catch (error: any) {
733
+ this.pending.next(false);
659
734
  this.parameters.updateIsLoadingModalVisible(false);
660
735
  this.error = `Unable to create room. ${error}`;
661
736
  }
@@ -665,6 +740,7 @@ export class PreJoinPage implements OnInit {
665
740
  this.parameters.updateSocket(this.initSocket!);
666
741
  await this.createLocalRoom({ createData: createData });
667
742
  } catch (error: any) {
743
+ this.pending.next(false);
668
744
  this.parameters.updateIsLoadingModalVisible(false);
669
745
  this.error = `Unable to create room. ${error}`;
670
746
  }
@@ -676,32 +752,44 @@ export class PreJoinPage implements OnInit {
676
752
  apiKey: this.credentials.apiKey,
677
753
  validate: true,
678
754
  });
755
+ this.pending.next(false);
679
756
  }
680
757
  }
681
758
 
682
759
  async handleJoinRoom(): Promise<void> {
683
- if (this.preJoinForm.invalid) {
684
- this.error = 'Please fill all the fields.';
760
+ if (this.pending.value) {
685
761
  return;
686
762
  }
763
+ this.pending.next(true);
764
+ let payload = {} as JoinMediaSFURoomOptions;
687
765
 
688
- const { name, eventID } = this.preJoinForm.value;
766
+ if (this.returnUI) {
767
+ const { name, eventID } = this.preJoinForm.value;
689
768
 
690
- if (!name || !eventID) {
691
- this.error = 'Please fill all the fields.';
692
- return;
693
- }
769
+ if (!name || !eventID) {
770
+ this.error = 'Please fill all the fields.';
771
+ return;
772
+ }
694
773
 
695
- const payload = {
696
- action: 'join',
697
- meetingID: eventID,
698
- userName: name,
699
- };
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
+ }
700
788
 
701
789
  if (this.localLink && !this.localLink.includes('mediasfu.com')) {
702
- const joinData = {
703
- eventID: eventID,
704
- userName: name,
790
+ const joinData: JoinLocalEventRoomParameters = {
791
+ eventID: payload.meetingID,
792
+ userName: payload.userName,
705
793
  secureCode: '',
706
794
  videoPreference: null,
707
795
  audioPreference: null,
@@ -709,15 +797,20 @@ export class PreJoinPage implements OnInit {
709
797
  };
710
798
 
711
799
  await this.joinLocalRoom({ joinData: joinData });
800
+ this.pending.next(false);
712
801
  return;
713
802
  }
714
803
 
715
804
  this.parameters.updateIsLoadingModalVisible(true);
716
805
  try {
717
- const response = await this.joinRoomService.joinRoomOnMediaSFU({
806
+ if (!this.joinMediaSFURoom) {
807
+ this.joinMediaSFURoom = this.joinRoomService.joinRoomOnMediaSFU;
808
+ }
809
+ const response = await this.joinMediaSFURoom({
718
810
  payload,
719
811
  apiUserName: this.credentials.apiUserName,
720
812
  apiKey: this.credentials.apiKey,
813
+ localLink: this.localLink,
721
814
  });
722
815
 
723
816
  if (response.success && response.data && 'roomName' in response.data) {
@@ -725,13 +818,15 @@ export class PreJoinPage implements OnInit {
725
818
  apiUserName: response.data.roomName,
726
819
  apiToken: response.data.secret,
727
820
  link: response.data.link,
728
- userName: name,
821
+ userName: payload.userName,
729
822
  parameters: this.parameters,
730
823
  validate: true,
731
824
  });
732
825
  this.error = '';
826
+ this.pending.next(false);
733
827
  } else {
734
828
  this.parameters.updateIsLoadingModalVisible(false);
829
+ this.pending.next(false);
735
830
  this.error = `Unable to connect to room. ${
736
831
  response.data ? ('error' in response.data ? response.data.error : '') : ''
737
832
  }`;
@@ -742,7 +837,6 @@ export class PreJoinPage implements OnInit {
742
837
  }
743
838
  }
744
839
  }
745
-
746
840
  ```
747
841
 
748
842
  ### IP Blockage Warning And Local UI Development
@@ -757,8 +851,7 @@ In this mode, the module will operate locally without making requests to MediaSF
757
851
 
758
852
  ### Example for Generic UI to Render Broadcast Room
759
853
 
760
- ```javascript
761
-
854
+ ```typescript
762
855
  import { Component, OnInit } from '@angular/core';
763
856
  import {
764
857
  MediasfuBroadcast,
@@ -863,7 +956,7 @@ export class AppComponent implements OnInit {
863
956
 
864
957
  ### Example for Generic View
865
958
 
866
- ```javascript
959
+ ```typescript
867
960
  import { Component, OnInit } from '@angular/core';
868
961
  import {
869
962
  GenerateRandomParticipants,
@@ -878,44 +971,41 @@ import {
878
971
  PreJoinPage,
879
972
  } from 'mediasfu-angular';
880
973
 
974
+ // Assume all missing imports are similar to the previous example
975
+
881
976
 
882
977
  /**
883
- * The main application component for MediaSFU.
884
- *
885
- * This component initializes the necessary configuration and credentials for the MediaSFU application.
886
- * Users can specify their own Community Edition (CE) server, utilize MediaSFU Cloud by default, or enable MediaSFU Cloud for egress features.
978
+ * AppComponent
887
979
  *
888
- * @remarks
889
- * - **Using Your Own Community Edition (CE) Server**: Set the `localLink` to point to your CE server.
890
- * - **Using MediaSFU Cloud by Default**: If not using a custom server (`localLink` is empty), the application connects to MediaSFU Cloud.
891
- * - **MediaSFU Cloud Egress Features**: To enable cloud recording, capturing, and returning real-time images and audio buffers,
892
- * set `connectMediaSFU` to `true` in addition to specifying your `localLink`.
893
- * - **Credentials Requirement**: If not using your own server, provide `apiUserName` and `apiKey`. The same applies when using MediaSFU Cloud for egress.
894
- * - **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.
895
985
  *
896
- * @component
897
- * @example
898
- * ```typescript
899
- * // Example usage of the AppComponent
900
- * @NgModule({
901
- * declarations: [AppComponent],
902
- * imports: [BrowserModule, MediasfuWebinar],
903
- * bootstrap: [AppComponent]
904
- * })
905
- * export class AppModule { }
906
- * ```
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.
907
992
  */
908
993
  @Component({
909
994
  selector: 'app-root',
910
- imports: [MediasfuWebinar],
995
+ imports: [MediasfuGeneric],
911
996
  template: `
912
- <app-mediasfu-webinar
913
- [credentials]="credentials"
914
- [localLink]="localLink"
915
- [connectMediaSFU]="connectMediaSFU"
916
- [PrejoinPage]="PreJoinPage"
917
- [seedData]="seedData">
918
- </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>
919
1009
  `,
920
1010
  providers: [
921
1011
  GenerateRandomParticipants,
@@ -925,54 +1015,187 @@ import {
925
1015
  ],
926
1016
  })
927
1017
  export class AppComponent implements OnInit {
928
- // ========================
929
- // ====== CONFIGURATION ======
930
- // ========================
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
+ */
931
1030
 
932
- /**
933
- * Mediasfu account credentials.
934
- * Replace 'your_api_username' and 'your_api_key' with your actual credentials.
935
- * Not needed if using a custom server without MediaSFU Cloud Egress features.
936
- */
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
+ /*
937
1035
  credentials = {
938
- apiUserName: 'your_api_username',
939
- apiKey: 'your_api_key',
1036
+ apiUserName: 'dummyUsr', // 8 characters
1037
+ apiKey: '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', // 64 characters
940
1038
  };
1039
+ localLink = 'http://your-ce-server.com'; // e.g., 'http://localhost:3000'
1040
+ connectMediaSFU = localLink.trim() !== '';
1041
+ */
941
1042
 
942
- /**
943
- * Specify your Community Edition (CE) server link.
944
- * Leave as an empty string if not using a custom server.
945
- */
946
- 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 } = {};
947
1082
 
948
1083
  /**
949
- * Automatically set `connectMediaSFU` to `true` if `localLink` is provided,
950
- * indicating the use of MediaSFU Cloud by default.
1084
+ * Function to update sourceParameters state.
951
1085
  *
952
- * - If `localLink` is not empty, MediaSFU Cloud will be used for additional features.
953
- * - If `localLink` is empty, the application will connect to MediaSFU Cloud by default.
1086
+ * @param data - The data to update sourceParameters with.
954
1087
  */
955
- connectMediaSFU = this.localLink.trim() !== ''; // set to false if not using MediaSFU Cloud for Main Server or Egress
956
-
957
- // ========================
958
- // ====== USE CASES ======
959
- // ========================
1088
+ updateSourceParameters = (data: { [key: string]: any }) => {
1089
+ this.sourceParameters = data;
1090
+ };
960
1091
 
961
- // Deprecated Feature: useLocalUIMode
962
- // This feature is deprecated due to updates for strong typing.
963
- // 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
+ // );
964
1187
 
965
1188
  /**
966
- * Uncomment and configure the following section if you intend to use seed data
967
- * for generating random participants and messages.
1189
+ * Default Rendering: MediasfuGeneric with Updated Configuration
968
1190
  *
969
- * 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.
970
1193
  */
971
- /*
972
- useSeed = false;
973
- seedData: any = {};
974
-
975
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
+ /*
976
1199
  if (this.useSeed) {
977
1200
  const memberName = 'Alice';
978
1201
  const hostName = 'Fred';
@@ -1018,8 +1241,8 @@ export class AppComponent implements OnInit {
1018
1241
 
1019
1242
  // Determine whether to use local UI mode
1020
1243
  this.useLocalUIMode = this.useSeed;
1244
+ */
1021
1245
  }
1022
- */
1023
1246
 
1024
1247
  // ========================
1025
1248
  // ====== COMPONENT SELECTION ======
@@ -1096,7 +1319,6 @@ export class AppComponent implements OnInit {
1096
1319
  * Renders the MediasfuWebinar component with specified server and cloud connection settings.
1097
1320
  * This is the default use case if no specific event type is selected.
1098
1321
  */
1099
- seedData: any = {}; // Initialize seedData as empty object
1100
1322
 
1101
1323
  // Reference to the PreJoinPage component
1102
1324
  PreJoinPage = PreJoinPage;
@@ -1105,7 +1327,9 @@ export class AppComponent implements OnInit {
1105
1327
  private generateRandomParticipants: GenerateRandomParticipants,
1106
1328
  private generateRandomMessages: GenerateRandomMessages,
1107
1329
  private generateRandomRequestList: GenerateRandomRequestList,
1108
- private generateRandomWaitingRoomList: GenerateRandomWaitingRoomList
1330
+ private generateRandomWaitingRoomList: GenerateRandomWaitingRoomList,
1331
+ public createRoomOnMediaSFU: CreateRoomOnMediaSFU,
1332
+ public joinRoomOnMediaSFU: JoinRoomOnMediaSFU
1109
1333
  ) { }
1110
1334
 
1111
1335
  // Deprecated Feature: useSeed and seedData for generating random participants and messages
@@ -1115,59 +1339,203 @@ export class AppComponent implements OnInit {
1115
1339
  // eventType = 'webinar';
1116
1340
  // useLocalUIMode = false;
1117
1341
 
1118
- ngOnInit(): void {
1119
- // If using seed data, generate random participants and messages - DEPRECATED FEATURE
1120
- // Note: This feature is deprecated and maintained only for legacy purposes.
1121
- // Uncomment and configure the following section if you intend to use seed data
1122
-
1123
- // if (this.useSeed) {
1124
- // const memberName = 'Alice';
1125
- // const hostName = 'Fred';
1126
-
1127
- // // Generate random participants
1128
- // const participants_ = this.generateRandomParticipants.generateRandomParticipants({
1129
- // member: memberName,
1130
- // coHost: '',
1131
- // host: hostName,
1132
- // forChatBroadcast: this.eventType === 'broadcast' || this.eventType === 'chat',
1133
- // });
1134
-
1135
- // // Generate random messages
1136
- // const messages_ = this.generateRandomMessages.generateRandomMessages({
1137
- // participants: participants_,
1138
- // member: memberName,
1139
- // host: hostName,
1140
- // forChatBroadcast: this.eventType === 'broadcast' || this.eventType === 'chat',
1141
- // });
1142
-
1143
- // // Generate random request list
1144
- // const requests_ = this.generateRandomRequestList.generateRandomRequestList({
1145
- // participants: participants_,
1146
- // hostName: memberName,
1147
- // coHostName: '',
1148
- // numberOfRequests: 3,
1149
- // });
1150
-
1151
- // // Generate random waiting room list
1152
- // const waitingList_ = this.generateRandomWaitingRoomList.generateRandomWaitingRoomList();
1153
-
1154
- // // Assign generated data to seedData
1155
- // this.seedData = {
1156
- // participants: participants_,
1157
- // messages: messages_,
1158
- // requests: requests_,
1159
- // waitingList: waitingList_,
1160
- // member: memberName,
1161
- // host: hostName,
1162
- // eventType: this.eventType,
1163
- // };
1164
- // }
1165
-
1166
- // Determine whether to use local UI mode, deprecated feature
1167
- // this.useLocalUIMode = this.useSeed;
1168
- }
1169
1342
  }
1170
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
+ */
1171
1539
 
1172
1540
  ```
1173
1541