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