mirror of https://github.com/OpenVidu/openvidu.git
ov-components: add recordingStreamBaseUrl directive and integrate with config service for dynamic stream URL construction
parent
6137bdbbbc
commit
90fd0ef44e
|
@ -36,6 +36,7 @@ import {
|
||||||
MinimalDirective,
|
MinimalDirective,
|
||||||
ParticipantNameDirective,
|
ParticipantNameDirective,
|
||||||
PrejoinDirective,
|
PrejoinDirective,
|
||||||
|
RecordingStreamBaseUrlDirective,
|
||||||
TokenDirective,
|
TokenDirective,
|
||||||
TokenErrorDirective,
|
TokenErrorDirective,
|
||||||
VideoEnabledDirective
|
VideoEnabledDirective
|
||||||
|
@ -56,6 +57,7 @@ import {
|
||||||
PrejoinDisplayParticipantName,
|
PrejoinDisplayParticipantName,
|
||||||
VideoEnabledDirective,
|
VideoEnabledDirective,
|
||||||
AudioEnabledDirective,
|
AudioEnabledDirective,
|
||||||
|
RecordingStreamBaseUrlDirective,
|
||||||
ToolbarCameraButtonDirective,
|
ToolbarCameraButtonDirective,
|
||||||
ToolbarMicrophoneButtonDirective,
|
ToolbarMicrophoneButtonDirective,
|
||||||
ToolbarScreenshareButtonDirective,
|
ToolbarScreenshareButtonDirective,
|
||||||
|
@ -100,6 +102,7 @@ import {
|
||||||
PrejoinDisplayParticipantName,
|
PrejoinDisplayParticipantName,
|
||||||
VideoEnabledDirective,
|
VideoEnabledDirective,
|
||||||
AudioEnabledDirective,
|
AudioEnabledDirective,
|
||||||
|
RecordingStreamBaseUrlDirective,
|
||||||
ToolbarCameraButtonDirective,
|
ToolbarCameraButtonDirective,
|
||||||
ToolbarMicrophoneButtonDirective,
|
ToolbarMicrophoneButtonDirective,
|
||||||
ToolbarScreenshareButtonDirective,
|
ToolbarScreenshareButtonDirective,
|
||||||
|
|
|
@ -725,3 +725,69 @@ export class AudioEnabledDirective implements OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The **recordingStreamBaseUrl** directive sets the base URL for retrieving recording streams.
|
||||||
|
* The complete request URL is dynamically constructed by concatenating the supplied URL, the
|
||||||
|
* internally managed recordingId, and the `/stream` segment.
|
||||||
|
*
|
||||||
|
* The final URL format will be:
|
||||||
|
*
|
||||||
|
* {recordingStreamBaseUrl}/{recordingId}/stream
|
||||||
|
*
|
||||||
|
* Default: `"/{recordingId}/stream"`
|
||||||
|
*
|
||||||
|
* It is essential that the resulting route is declared and configured on your backend, as it is
|
||||||
|
* used for serving and accessing the recording streams.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* <ov-videoconference [recordingStreamBaseUrl]="'https://myserver.com/api/recordings'">
|
||||||
|
* </ov-videoconference>
|
||||||
|
*/
|
||||||
|
@Directive({
|
||||||
|
selector: 'ov-videoconference[recordingStreamBaseUrl]'
|
||||||
|
})
|
||||||
|
export class RecordingStreamBaseUrlDirective implements AfterViewInit, OnDestroy {
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
@Input() set recordingStreamBaseUrl(url: string) {
|
||||||
|
this.update(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private elementRef: ElementRef,
|
||||||
|
private libService: OpenViduComponentsConfigService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
this.update(this.recordingStreamBaseUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
clear() {
|
||||||
|
this.update('');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
update(value: string) {
|
||||||
|
if (value) this.libService.setRecordingStreamBaseUrl(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -33,6 +33,9 @@ export class OpenViduComponentsConfigService {
|
||||||
private audioEnabled = <BehaviorSubject<boolean>>new BehaviorSubject(true);
|
private audioEnabled = <BehaviorSubject<boolean>>new BehaviorSubject(true);
|
||||||
audioEnabled$: Observable<boolean>;
|
audioEnabled$: Observable<boolean>;
|
||||||
|
|
||||||
|
private recordingStreamBaseUrl = <BehaviorSubject<string>>new BehaviorSubject('');
|
||||||
|
recordingStreamBaseUrl$: Observable<string>;
|
||||||
|
|
||||||
//Toolbar settings
|
//Toolbar settings
|
||||||
private cameraButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
|
private cameraButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
|
||||||
cameraButton$: Observable<boolean>;
|
cameraButton$: Observable<boolean>;
|
||||||
|
@ -121,6 +124,7 @@ export class OpenViduComponentsConfigService {
|
||||||
this.prejoinDisplayParticipantName$ = this.prejoinDisplayParticipantName.asObservable();
|
this.prejoinDisplayParticipantName$ = this.prejoinDisplayParticipantName.asObservable();
|
||||||
this.videoEnabled$ = this.videoEnabled.asObservable();
|
this.videoEnabled$ = this.videoEnabled.asObservable();
|
||||||
this.audioEnabled$ = this.audioEnabled.asObservable();
|
this.audioEnabled$ = this.audioEnabled.asObservable();
|
||||||
|
this.recordingStreamBaseUrl$ = this.recordingStreamBaseUrl.asObservable();
|
||||||
//Toolbar observables
|
//Toolbar observables
|
||||||
this.cameraButton$ = this.cameraButton.asObservable();
|
this.cameraButton$ = this.cameraButton.asObservable();
|
||||||
this.microphoneButton$ = this.microphoneButton.asObservable();
|
this.microphoneButton$ = this.microphoneButton.asObservable();
|
||||||
|
@ -214,6 +218,17 @@ export class OpenViduComponentsConfigService {
|
||||||
return this.audioEnabled.getValue();
|
return this.audioEnabled.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setRecordingStreamBaseUrl(recordingStreamBaseUrl: string) {
|
||||||
|
this.recordingStreamBaseUrl.next(recordingStreamBaseUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRecordingStreamBaseUrl(): string {
|
||||||
|
let baseUrl = this.recordingStreamBaseUrl.getValue();
|
||||||
|
// Add trailing slash if not present
|
||||||
|
baseUrl += baseUrl.endsWith('/') ? '' : '/';
|
||||||
|
return baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
//Toolbar settings
|
//Toolbar settings
|
||||||
|
|
||||||
setCameraButton(cameraButton: boolean) {
|
setCameraButton(cameraButton: boolean) {
|
||||||
|
|
|
@ -2,7 +2,9 @@ import { Injectable } from '@angular/core';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { RecordingInfo, RecordingStatus, RecordingStatusInfo } from '../../models/recording.model';
|
import { RecordingInfo, RecordingStatus, RecordingStatusInfo } from '../../models/recording.model';
|
||||||
import { ActionService } from '../action/action.service';
|
import { ActionService } from '../action/action.service';
|
||||||
import { GlobalConfigService } from '../config/global-config.service';
|
import { LoggerService } from '../logger/logger.service';
|
||||||
|
import { ILogger } from '../../models/logger.model';
|
||||||
|
import { OpenViduComponentsConfigService } from '../config/directive-config.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
@ -13,19 +15,19 @@ export class RecordingService {
|
||||||
*/
|
*/
|
||||||
recordingStatusObs: Observable<RecordingStatusInfo>;
|
recordingStatusObs: Observable<RecordingStatusInfo>;
|
||||||
private recordingTimeInterval: NodeJS.Timeout;
|
private recordingTimeInterval: NodeJS.Timeout;
|
||||||
private API_RECORDINGS_PREFIX = 'call/api/recordings/';
|
|
||||||
private recordingStatus = <BehaviorSubject<RecordingStatusInfo>>new BehaviorSubject({
|
private recordingStatus = <BehaviorSubject<RecordingStatusInfo>>new BehaviorSubject({
|
||||||
status: RecordingStatus.STOPPED,
|
status: RecordingStatus.STOPPED,
|
||||||
recordingList: [] as RecordingInfo[],
|
recordingList: [] as RecordingInfo[],
|
||||||
recordingElapsedTime: new Date(0, 0, 0, 0, 0, 0, 0)
|
recordingElapsedTime: new Date(0, 0, 0, 0, 0, 0, 0)
|
||||||
});
|
});
|
||||||
|
private log: ILogger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
constructor(private actionService: ActionService, private globalService: GlobalConfigService) {
|
constructor(private actionService: ActionService, private libService: OpenViduComponentsConfigService, private loggerService: LoggerService) {
|
||||||
|
this.log = this.loggerService.get('RecordingService');
|
||||||
this.recordingStatusObs = this.recordingStatus.asObservable();
|
this.recordingStatusObs = this.recordingStatus.asObservable();
|
||||||
this.API_RECORDINGS_PREFIX = this.globalService.getBaseHref() + this.API_RECORDINGS_PREFIX;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -146,9 +148,10 @@ export class RecordingService {
|
||||||
*/
|
*/
|
||||||
playRecording(recording: RecordingInfo) {
|
playRecording(recording: RecordingInfo) {
|
||||||
// Only COMPOSED recording is supported. The extension will allways be 'mp4'.
|
// Only COMPOSED recording is supported. The extension will allways be 'mp4'.
|
||||||
console.log('Playing recording', recording);
|
this.log.d('Playing recording', recording);
|
||||||
const queryParamForAvoidCache = `?t=${new Date().getTime()}`;
|
const queryParamForAvoidCache = `?t=${new Date().getTime()}`;
|
||||||
const streamRecordingUrl = `${this.API_RECORDINGS_PREFIX}${recording.id}/stream${queryParamForAvoidCache}`;
|
const baseUrl = this.libService.getRecordingStreamBaseUrl();
|
||||||
|
const streamRecordingUrl = `${baseUrl}${recording.id}/stream${queryParamForAvoidCache}`;
|
||||||
this.actionService.openRecordingPlayerDialog(streamRecordingUrl);
|
this.actionService.openRecordingPlayerDialog(streamRecordingUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +164,8 @@ export class RecordingService {
|
||||||
// Only COMPOSED recording is supported. The extension will allways be 'mp4'.
|
// Only COMPOSED recording is supported. The extension will allways be 'mp4'.
|
||||||
const queryParamForAvoidCache = `?t=${new Date().getTime()}`;
|
const queryParamForAvoidCache = `?t=${new Date().getTime()}`;
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = `${this.API_RECORDINGS_PREFIX}${recording.id}/stream${queryParamForAvoidCache}`;
|
const baseUrl = this.libService.getRecordingStreamBaseUrl();
|
||||||
|
link.href = `${baseUrl}${recording.id}/stream${queryParamForAvoidCache}`;
|
||||||
link.download = recording.filename || 'openvidu-recording.mp4';
|
link.download = recording.filename || 'openvidu-recording.mp4';
|
||||||
link.dispatchEvent(
|
link.dispatchEvent(
|
||||||
new MouseEvent('click', {
|
new MouseEvent('click', {
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
[participantName]="'Participant'"
|
[participantName]="'Participant'"
|
||||||
[videoEnabled]="true"
|
[videoEnabled]="true"
|
||||||
[audioEnabled]="true"
|
[audioEnabled]="true"
|
||||||
|
[recordingStreamBaseUrl]="'call/api/recordings'"
|
||||||
[toolbarCameraButton]="true"
|
[toolbarCameraButton]="true"
|
||||||
[toolbarMicrophoneButton]="true"
|
[toolbarMicrophoneButton]="true"
|
||||||
[toolbarScreenshareButton]="true"
|
[toolbarScreenshareButton]="true"
|
||||||
|
|
Loading…
Reference in New Issue