diff --git a/openvidu-components-angular/projects/openvidu-angular/package.json b/openvidu-components-angular/projects/openvidu-angular/package.json index 55dfcc9b..1b2ff923 100644 --- a/openvidu-components-angular/projects/openvidu-angular/package.json +++ b/openvidu-components-angular/projects/openvidu-angular/package.json @@ -9,6 +9,7 @@ "@angular/forms": "^13.0.0", "@angular/flex-layout": "^13.0.0-beta.36", "autolinker": "^3.14.3", + "buffer": "^6.0.3", "openvidu-browser": "^2.20.0" }, "dependencies": { diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.css b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.css new file mode 100644 index 00000000..b7bffcca --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.css @@ -0,0 +1,23 @@ +#call-container, #room-container { + height: 100%; +} + +#user-settings-container { + height: inherit; +} + +.error-icon { + color: var(--ov-warn-color); +} + +#spinner { + position: absolute; + top: 40%; + bottom: 0; + left: 0; + right: 0; + margin: auto; + text-align: -webkit-center; + text-align: -moz-center; + color: var(--ov-primary-color); +} \ No newline at end of file diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.html b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.html new file mode 100644 index 00000000..6c793893 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.html @@ -0,0 +1,30 @@ +
+
+ +
+ +
+ + Joining the room ... +
+ +
+ error + {{errorMessage}} +
+ +
+ + + + + + +
+
diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.spec.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.spec.ts new file mode 100644 index 00000000..faa9c113 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { VideoconferenceComponent } from './videoconference.component'; + +describe('VideoconferenceComponent', () => { + let component: VideoconferenceComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ VideoconferenceComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(VideoconferenceComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.ts new file mode 100644 index 00000000..23050514 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.ts @@ -0,0 +1,72 @@ +import { Component, Input, OnInit, Output } from '@angular/core'; +import { RestService } from '../../services/rest/rest.service'; + +@Component({ + selector: 'ov-videoconference', + templateUrl: './videoconference.component.html', + styleUrls: ['./videoconference.component.css'] +}) +export class VideoconferenceComponent implements OnInit { + @Input() sessionName: string; + @Input() userName: string; + @Input() openviduServerUrl: string; + @Input() openviduSecret: string; + @Input() tokens: string[]; + + joinSessionClicked: boolean = false; + closeClicked: boolean = false; + isSessionAlive: boolean = false; + _tokens: { webcam: string; screen: string }; + error: boolean = false; + errorMessage: string = ''; + + constructor(private restService: RestService) {} + + ngOnInit() {} + + async onJoinClicked() { + + if (!this.tokens || this.tokens?.length === 0) { + //No tokens received + + if (!!this.sessionName && !!this.openviduServerUrl && !!this.openviduSecret) { + // Generate tokens + this._tokens = { + webcam: await this.restService.getToken(this.sessionName, this.openviduServerUrl, this.openviduSecret), + screen: await this.restService.getToken(this.sessionName, this.openviduServerUrl, this.openviduSecret) + }; + } else { + // No tokens received and can't generate them + this.error = true; + this.errorMessage = `Cannot access to OpenVidu Server with url '${this.openviduServerUrl}' to genere tokens for session '${this.sessionName}'`; + throw this.errorMessage; + } + } else if (this.tokens?.length < 2) { + // 1 token received + this._tokens = { + webcam: this.tokens[0], + screen: await this.restService.getToken(this.sessionName, this.openviduServerUrl, this.openviduSecret) + }; + } else { + // 2 tokens received. + this._tokens = { + webcam: this.tokens[0], + screen: this.tokens[1] + }; + } + this.joinSessionClicked = true; + this.isSessionAlive = true; + } + onLeaveSessionClicked() { + this.isSessionAlive = false; + this.closeClicked = true; + } + + onMicClicked() {} + + onCamClicked() {} + + onScreenShareClicked() {} + + onSpeakerLayoutClicked() {} +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/openvidu-angular.module.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/openvidu-angular.module.ts index 92eecb8b..f2b2d23a 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/openvidu-angular.module.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/openvidu-angular.module.ts @@ -56,31 +56,33 @@ import { SidenavMenuService } from './services/sidenav-menu/sidenav-menu.service import { ParticipantService } from './services/participant/participant.service'; import { ParticipantItemComponent } from './components/participants-panel/participant-item/participant-item.component'; import { ParticipantPanelComponent } from './components/participants-panel/participant-panel/participant-panel.component'; +import { VideoconferenceComponent } from './components/videoconference/videoconference.component'; @NgModule({ - declarations: [ - UserSettingsComponent, - VideoComponent, - ToolbarComponent, - ChatComponent, - RoomComponent, - LayoutComponent, - ParticipantComponent, - DialogTemplateComponent, - LinkifyPipe, - TooltipListPipe, - ParticipantConnectionsPipe, - ConnectionsEnabledPipe, - NicknamePipe, - ParticipantItemComponent, - ParticipantPanelComponent - ], - imports: [ - CommonModule, - HttpClientModule, - FormsModule, + declarations: [ + UserSettingsComponent, + VideoComponent, + ToolbarComponent, + ChatComponent, + RoomComponent, + LayoutComponent, + ParticipantComponent, + DialogTemplateComponent, + LinkifyPipe, + TooltipListPipe, + ParticipantConnectionsPipe, + ConnectionsEnabledPipe, + NicknamePipe, + ParticipantItemComponent, + ParticipantPanelComponent, + VideoconferenceComponent + ], + imports: [ + CommonModule, + HttpClientModule, + FormsModule, ReactiveFormsModule, - RouterModule.forRoot([]), + RouterModule.forRoot([]), MatButtonModule, MatCardModule, MatToolbarModule, @@ -99,53 +101,46 @@ import { ParticipantPanelComponent } from './components/participants-panel/parti MatSnackBarModule, FlexLayoutModule, MatMenuModule, - MatDividerModule, - MatListModule - ], - providers: [ - ActionService, - CdkOverlayContainer, + MatDividerModule, + MatListModule + ], + providers: [ + ActionService, + CdkOverlayContainer, { provide: OverlayContainer, useClass: CdkOverlayContainer }, - ChatService, - SidenavMenuService, - DeviceService, - DocumentService, - LayoutService, - LoggerService, - PlatformService, - ParticipantService, - StorageService, - TokenService, - WebrtcService - - ], - exports: [ - UserSettingsComponent, - ToolbarComponent, - ChatComponent, - RoomComponent, - LayoutComponent, - ParticipantComponent, - VideoComponent, - ParticipantConnectionsPipe, - CommonModule - ], - entryComponents: [ - DialogTemplateComponent - ] + ChatService, + SidenavMenuService, + DeviceService, + DocumentService, + LayoutService, + LoggerService, + PlatformService, + ParticipantService, + StorageService, + TokenService, + WebrtcService + ], + exports: [ + VideoconferenceComponent, + UserSettingsComponent, + ToolbarComponent, + ChatComponent, + RoomComponent, + LayoutComponent, + ParticipantComponent, + VideoComponent, + ParticipantConnectionsPipe, + CommonModule + ], + entryComponents: [DialogTemplateComponent] }) - - export class OpenviduAngularModule { - static forRoot(environment): ModuleWithProviders { - - // console.log(`${library.name} config: ${environment}`); - const libConfig: LibConfig = { environment }; - return { - ngModule: OpenviduAngularModule, - providers: [LibraryConfigService , {provide: 'LIB_CONFIG', useValue: libConfig}] - }; - } - - - } + static forRoot(environment): ModuleWithProviders { + // console.log(`${library.name} config: ${environment}`); + const libConfig: LibConfig = { environment }; + return { + ngModule: OpenviduAngularModule, + providers: [LibraryConfigService, { provide: 'LIB_CONFIG', useValue: libConfig }] + }; + } +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/rest/rest.service.spec.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/rest/rest.service.spec.ts new file mode 100644 index 00000000..c0a594f3 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/rest/rest.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { RestService } from './rest.service'; + +describe('RestService', () => { + let service: RestService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(RestService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/rest/rest.service.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/rest/rest.service.ts new file mode 100644 index 00000000..8a11a50f --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/rest/rest.service.ts @@ -0,0 +1,75 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { catchError, throwError } from 'rxjs'; +import { Buffer} from 'buffer'; + +@Injectable({ + providedIn: 'root' +}) +export class RestService { + constructor(private http: HttpClient) {} + + async getToken(sessionId: string, openviduServerUrl: string, openviduSecret: string): Promise { + if (!!openviduServerUrl && !!openviduSecret) { + const _sessionId = await this.createSession(sessionId, openviduServerUrl, openviduSecret); + return await this.createToken(_sessionId, openviduServerUrl, openviduSecret); + } else { + return Promise.reject(`Error requesting a token to ${openviduServerUrl} with session id: ${sessionId}`); + } + } + + private createSession(sessionId: string, openviduServerUrl: string, openviduSecret: string): Promise { + return new Promise((resolve, reject) => { + const body = JSON.stringify({ customSessionId: sessionId }); + const options = { + headers: new HttpHeaders({ + Authorization: 'Basic ' + this.btoa('OPENVIDUAPP:' + openviduSecret), + 'Content-Type': 'application/json' + }) + }; + return this.http + .post(openviduServerUrl + '/openvidu/api/sessions', body, options) + .pipe( + catchError((error) => { + if (error.status === 409) { + resolve(sessionId); + } + if (error.statusText === 'Unknown Error') { + reject({ status: 401, message: 'ERR_CERT_AUTHORITY_INVALID' }); + } + return throwError(() => new Error(error)); + }) + ) + .subscribe((response) => { + resolve(response.id); + }); + }); + } + + private createToken(sessionId: string, openviduServerUrl: string, openviduSecret: string): Promise { + return new Promise((resolve, reject) => { + const body = JSON.stringify({}); + const options = { + headers: new HttpHeaders({ + Authorization: 'Basic ' + this.btoa('OPENVIDUAPP:' + openviduSecret), + 'Content-Type': 'application/json' + }) + }; + return this.http + .post(openviduServerUrl + '/openvidu/api/sessions/' + sessionId + '/connection', body, options) + .pipe( + catchError((error) => { + reject(error); + return throwError(() => new Error(error)); + }) + ) + .subscribe((response) => { + resolve(response.token); + }); + }); + } + + private btoa(str: string): string { + return Buffer.from(str).toString('base64'); + } +} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/public-api.ts b/openvidu-components-angular/projects/openvidu-angular/src/public-api.ts index 311c36b3..1d037218 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/public-api.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/public-api.ts @@ -21,6 +21,7 @@ export * from './lib/services/cdk-overlay/cdk-overlay.service'; export * from './lib/services/storage/storage.service'; // Components +export * from './lib/components/videoconference/videoconference.component'; export * from './lib/components/user-settings/user-settings.component'; export * from './lib/components/toolbar/toolbar.component'; export * from './lib/components/chat/chat.component';