openvidu-call: Added recording

This commit is contained in:
csantosm 2022-04-29 12:31:36 +02:00
parent c8cd12c2b1
commit 0dc070bb38
13 changed files with 1620 additions and 903 deletions

File diff suppressed because it is too large Load Diff

View File

@ -4,14 +4,19 @@
"url": "https://github.com/OpenVidu/openvidu-call/issues"
},
"dependencies": {
"axios": "0.27.2",
"btoa": "1.2.1",
"cookie-parser": "1.4.6",
"cookie-session": "^2.0.0",
"dotenv": "16.0.0",
"express": "4.17.3",
"express": "4.18.1",
"http-proxy-middleware": "2.0.6",
"openvidu-node-client": "2.22.0"
},
"description": "OpenVidu Call Server",
"devDependencies": {
"@types/btoa": "1.2.3",
"@types/cookie-parser": "1.4.3",
"@types/express": "4.17.13",
"@types/jest": "27.4.0",
"@types/node": "17.0.10",

View File

@ -1,29 +1,45 @@
import * as express from 'express';
import { SERVER_PORT, OPENVIDU_URL, OPENVIDU_SECRET, CALL_OPENVIDU_CERTTYPE } from './config';
import {app as callController} from './controllers/CallController';
import { SERVER_PORT, OPENVIDU_URL, OPENVIDU_SECRET, CALL_OPENVIDU_CERTTYPE, ADMIN_SECRET, RECORDING } from './config';
import { app as sessionController } from './controllers/SessionController';
import { app as adminController } from './controllers/AdminController';
import { app as recordingController, proxyGETRecording } from './controllers/RecordingController';
import * as dotenv from 'dotenv';
import * as cookieParser from 'cookie-parser';
import * as cookieSession from 'cookie-session';
dotenv.config();
const app = express();
app.use(express.static(__dirname + '/public'));
app.use(express.json());
app.use(cookieParser());
app.use(
cookieSession({
name: 'session',
keys: [ADMIN_SECRET],
maxAge: 24 * 60 * 60 * 1000 // 24 hours
})
);
app.use('/call', callController);
app.use('/sessions', sessionController);
app.use('/recordings', recordingController);
app.use('/recordings/:recordingId', proxyGETRecording);
app.use('/admin', adminController);
// Accept selfsigned certificates if CALL_OPENVIDU_CERTTYPE=selfsigned
if (CALL_OPENVIDU_CERTTYPE === 'selfsigned') {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
}
app.listen(SERVER_PORT, () => {
console.log("---------------------------------------------------------");
console.log(" ")
console.log(`OPENVIDU URL: ${OPENVIDU_URL}`);
console.log(`OPENVIDU SECRET: ${OPENVIDU_SECRET}`);
console.log(`CALL OPENVIDU CERTTYPE: ${CALL_OPENVIDU_CERTTYPE}`);
console.log(`OpenVidu Call Server is listening on port ${SERVER_PORT}`);
console.log(" ")
console.log("---------------------------------------------------------");
console.log('---------------------------------------------------------');
console.log(' ');
console.log(`OPENVIDU URL: ${OPENVIDU_URL}`);
console.log(`OPENVIDU SECRET: ${OPENVIDU_SECRET}`);
console.log(`CALL OPENVIDU CERTTYPE: ${CALL_OPENVIDU_CERTTYPE}`);
console.log(`CALL RECORDING: ${RECORDING}`);
console.log(`CALL ADMIN PASSWORD: ${ADMIN_SECRET}`);
console.log(`OpenVidu Call Server is listening on port ${SERVER_PORT}`);
console.log(' ');
console.log('---------------------------------------------------------');
});

View File

@ -1,4 +1,6 @@
export const SERVER_PORT = process.env.SERVER_PORT || 5000;
export const OPENVIDU_URL = process.env.OPENVIDU_URL || 'https://localhost:4443';
export const OPENVIDU_SECRET = process.env.OPENVIDU_SECRET || 'MY_SECRET';
export const CALL_OPENVIDU_CERTTYPE = process.env.CALL_OPENVIDU_CERTTYPE;
export const CALL_OPENVIDU_CERTTYPE = process.env.CALL_OPENVIDU_CERTTYPE || 'selfsigned';
export const ADMIN_SECRET = process.env.ADMIN_SECRET || OPENVIDU_SECRET;
export const RECORDING = process.env.RECORDING || 'ENABLED';

View File

@ -0,0 +1,43 @@
import * as express from 'express';
import * as crypto from 'crypto';
import { Request, Response } from 'express';
import { OpenViduService } from '../services/OpenViduService';
import { ADMIN_SECRET } from '../config';
export const app = express.Router({
strict: true
});
const openviduService = OpenViduService.getInstance();
app.post('/login', async (req: Request, res: Response) => {
const password = req.body.password;
const isAdminTokenValid = openviduService.adminTokens.includes(req['session']?.token);
const isAuthValid = password === ADMIN_SECRET || isAdminTokenValid;
if (isAuthValid) {
try {
if (!req['session']?.token || !openviduService.adminTokens.includes(req['session']?.token)) {
// Save session token
const token = crypto.randomBytes(32).toString('hex');
res.cookie(openviduService.ADMIN_TOKEN_NAME, token);
req['session'] = { token };
openviduService.adminTokens.push(token);
}
const recordings = await openviduService.listAllRecordings(true);
console.log(`${recordings.length} recordings found`);
res.status(200).send(JSON.stringify({ recordings }));
} catch (error) {
console.error(error);
res.status(500).send('Unexpected error getting recordings');
}
} else {
res.status(403).send('Permissions denied');
}
});
app.post('/logout', async (req: Request, res: Response) => {
openviduService.adminTokens = openviduService.adminTokens.filter((token) => token !== req['session'].token);
req['session'] = {};
});

View File

@ -1,38 +0,0 @@
import * as express from 'express';
import { Request, Response } from 'express';
import { Session } from 'openvidu-node-client';
import { OpenViduService } from '../services/OpenViduService';
export const app = express.Router({
strict: true
});
const openviduService = new OpenViduService();
app.post('/', async (req: Request, res: Response) => {
let sessionId: string = req.body.sessionId;
let nickname: string = req.body.nickname;
let createdSession: Session = null;
console.log('Session ID received', sessionId);
try {
createdSession = await openviduService.createSession(sessionId);
} catch (error) {
handleError(error, res);
return;
}
try {
const connection = await openviduService.createConnection(createdSession, nickname);
res.status(200).send(JSON.stringify(connection.token));
} catch (error) {
handleError(error, res);
}
});
function handleError(error: any, res: Response){
try {
let statusCode = parseInt(error.message);
res.status(parseInt(error.message)).send(`OpenVidu Server returned an error to OpenVidu Call Server: ${statusCode}`)
} catch (error) {
res.status(503).send('Cannot connect with OpenVidu Server');
}
}

View File

@ -0,0 +1,172 @@
import { CALL_OPENVIDU_CERTTYPE, OPENVIDU_URL, RECORDING } from '../config';
import * as express from 'express';
import { Request, Response } from 'express';
import { Recording } from 'openvidu-node-client';
import { OpenViduService } from '../services/OpenViduService';
import { createProxyMiddleware } from 'http-proxy-middleware';
export const app = express.Router({
strict: true
});
const openviduService = OpenViduService.getInstance();
app.get('/', async (req: Request, res: Response) => {
try {
const IS_RECORDING_ENABLED = RECORDING.toUpperCase() === 'ENABLED';
const sessionId = openviduService.getSessionIdFromCookie(req.cookies);
const isAdminDashboard = openviduService.adminTokens.includes(req['session'].token);
let recordings = [];
if ((!!sessionId && IS_RECORDING_ENABLED && openviduService.isValidToken(sessionId, req.cookies)) || isAdminDashboard) {
if (isAdminDashboard) {
recordings = await openviduService.listAllRecordings(true);
} else {
const date = openviduService.getDateFromCookie(req.cookies);
recordings = await openviduService.listRecordingsBySessionIdAndDate(sessionId, date);
}
console.log('a', recordings)
res.status(200).send(JSON.stringify(recordings));
} else {
const message = IS_RECORDING_ENABLED ? 'Permissions denied to drive recording' : 'Recording is disabled';
res.status(403).send(JSON.stringify({ message }));
}
} catch (error) {
console.log(error);
const code = Number(error?.message);
let message = 'Unexpected error getting all recordings';
if (code === 404) {
message = 'No recording exist for the session';
}
return res.status(Number(code) || 500).send(JSON.stringify({ message }));
}
});
app.post('/start', async (req: Request, res: Response) => {
try {
let sessionId: string = req.body.sessionId;
if (openviduService.isValidToken(sessionId, req.cookies)) {
let startingRecording: Recording = null;
console.log(`Starting recording in ${sessionId}`);
startingRecording = await openviduService.startRecording(sessionId);
openviduService.recordingMap.get(sessionId).recordingId = startingRecording.id;
res.status(200).send(JSON.stringify(startingRecording));
} else {
console.log(`Permissions denied for starting recording in session ${sessionId}`);
res.status(403).send(JSON.stringify({ message: 'Permissions denied to drive recording' }));
}
} catch (error) {
console.log(error);
const code = Number(error?.message);
let message = `Unexpected error starting recording`;
if (code === 409) {
message = 'The session is already being recorded.';
} else if (code === 501) {
message = 'OpenVidu Server recording module is disabled';
} else if (code === 406) {
message = 'The session has no connected participants';
}
return res.status(code || 500).send(JSON.stringify({ message }));
}
});
app.post('/stop', async (req: Request, res: Response) => {
try {
let sessionId: string = req.body.sessionId;
if (openviduService.isValidToken(sessionId, req.cookies)) {
const recordingId = openviduService.recordingMap.get(sessionId)?.recordingId;
if (!!recordingId) {
console.log(`Stopping recording in ${sessionId}`);
await openviduService.stopRecording(recordingId);
const date = openviduService.getDateFromCookie(req.cookies);
const recordingList = await openviduService.listRecordingsBySessionIdAndDate(sessionId, date);
openviduService.recordingMap.get(sessionId).recordingId = '';
res.status(200).send(JSON.stringify(recordingList));
} else {
res.status(404).send(JSON.stringify({ message: 'Session was not being recorded' }));
}
} else {
res.status(403).send(JSON.stringify({ message: 'Permissions denied to drive recording' }));
}
} catch (error) {
console.log(error);
const code = Number(error?.message);
let message = `Unexpected error stopping recording`;
if (code === 501) {
message = 'OpenVidu Server recording module is disabled';
} else if (code === 406) {
message = 'Recording has STARTING status. Wait until STARTED status before stopping the recording';
}
return res.status(code || 500).send(JSON.stringify({ message }));
}
});
app.delete('/delete/:recordingId', async (req: Request, res: Response) => {
try {
const sessionId = openviduService.getSessionIdFromCookie(req.cookies);
const isAdminDashboard = openviduService.adminTokens.includes(req['session'].token);
let recordings = [];
if ((!!sessionId && openviduService.isValidToken(sessionId, req.cookies)) || isAdminDashboard) {
console.log('DELETE RECORDING');
const recordingId: string = req.params.recordingId;
if (!recordingId) {
return res.status(400).send('Missing recording id parameter.');
}
await openviduService.deleteRecording(recordingId);
if (isAdminDashboard) {
recordings = await openviduService.listAllRecordings(true);
} else {
const date = openviduService.getDateFromCookie(req.cookies);
recordings = await openviduService.listRecordingsBySessionIdAndDate(sessionId, date);
}
res.status(200).send(JSON.stringify(recordings));
} else {
res.status(403).send(JSON.stringify({ message: 'Permissions denied to drive recording' }));
}
} catch (error) {
console.log(error);
const code = Number(error?.message);
let message = `Unexpected error deleting the recording`;
if (code === 409) {
message = 'The recording has STARTED status. Stop it before deletion.';
} else if (code === 501) {
message = 'OpenVidu Server recording module is disabled';
} else if (code === 409) {
message = 'No recording exists for the session';
}
return res.status(code).send(JSON.stringify({ message }));
}
});
export const proxyGETRecording = createProxyMiddleware({
target: `${OPENVIDU_URL}/openvidu/recordings/`,
secure: CALL_OPENVIDU_CERTTYPE !== 'selfsigned',
pathRewrite: (path, req) => {
return `${req.params.recordingId}/${req.params.recordingId}.mp4`;
},
onProxyReq: (proxyReq, req: Request, res: Response) => {
const isAdminDashboard = openviduService.adminTokens.includes(req['session'].token);
const sessionId = openviduService.getSessionIdFromCookie(req.cookies);
if ((!!sessionId && openviduService.isValidToken(sessionId, req.cookies)) || isAdminDashboard) {
const recordingId: string = req.params.recordingId;
if (!recordingId) {
return res.status(400).send(JSON.stringify({ message: 'Missing recording id parameter.' }));
} else {
proxyReq.setHeader('Authorization', openviduService.getBasicAuth());
proxyReq.setHeader('Range', 'bytes=0-');
}
} else {
return res.status(403).send(JSON.stringify({ message: 'Permissions denied to drive recording' }));
}
},
onError: (error, req: Request, res: Response) => {
console.log(error);
const code = Number(error?.message);
let message = 'Unexpected error downloading the recording';
if (code === 404) {
message = 'No recording exist for the session';
}
res.status(Number(code) || 500).send(JSON.stringify({ message }));
return res.end();
}
});

View File

@ -0,0 +1,68 @@
import * as express from 'express';
import * as crypto from 'crypto';
import { Request, Response } from 'express';
import { OpenViduRole, Session } from 'openvidu-node-client';
import { OpenViduService } from '../services/OpenViduService';
import { RECORDING } from '../config';
export const app = express.Router({
strict: true
});
const openviduService = OpenViduService.getInstance();
app.post('/', async (req: Request, res: Response) => {
try {
console.log('Session ID received', req.body.sessionId);
let sessionId: string = req.body.sessionId;
let nickname: string = req.body.nickname;
let date = null;
let sessionCreated: Session = await openviduService.createSession(sessionId);
const RECORDING_TOKEN_NAME = openviduService.RECORDING_TOKEN_NAME;
const IS_RECORDING_ENABLED = RECORDING.toUpperCase() === 'ENABLED';
const hasValidToken = openviduService.isValidToken(sessionId, req.cookies);
const isSessionCreator = hasValidToken || sessionCreated.activeConnections.length === 0;
const role: OpenViduRole = isSessionCreator && IS_RECORDING_ENABLED ? OpenViduRole.MODERATOR : OpenViduRole.PUBLISHER;
const response = {cameraToken : '', screenToken: '', recording: IS_RECORDING_ENABLED};
const cameraConnection = await openviduService.createConnection(sessionCreated, nickname, role);
const screenConnection = await openviduService.createConnection(sessionCreated, nickname, role);
response.cameraToken = cameraConnection.token;
response.screenToken = screenConnection.token;
if (IS_RECORDING_ENABLED && isSessionCreator && !hasValidToken) {
/**
* ! *********** WARN *********** !
*
* To identify who is able to manage session recording, the code sends a cookie with a token to the session creator.
* The relation between cookies and sessions are stored in backend memory.
*
* This authentication & autorization system is pretty basic and it is not for production.
* We highly recommend IMPLEMENT YOUR OWN USER MANAGEMENT with persistence for a properly and secure recording feature.
*
* ! *********** WARN *********** !
**/
const uuid = crypto.randomBytes(32).toString('hex');
date = Date.now();
const recordingToken = `${response.cameraToken}&${RECORDING_TOKEN_NAME}=${uuid}&createdAt=${date}`;
res.cookie(RECORDING_TOKEN_NAME, recordingToken);
openviduService.recordingMap.set(sessionId, { token: recordingToken, recordingId: '' });
}
if(IS_RECORDING_ENABLED){
date = date || openviduService.getDateFromCookie(req.cookies);
response['recordings'] = await openviduService.listRecordingsBySessionIdAndDate(sessionId, date);
}
res.status(200).send(JSON.stringify(response));
} catch (error) {
console.error(error);
let message = 'Cannot connect with OpenVidu Server';
if(error.message === 500){
message = 'Unexpected error when creating the Connection object.'
} else if (error.message === 404){
message = 'No session exists';
}
res.status(error?.message || 503).send({ message });
}
});

View File

@ -1,27 +1,120 @@
import { Connection, ConnectionProperties, OpenVidu, Session, SessionProperties } from "openvidu-node-client";
import { Connection, ConnectionProperties, OpenVidu, OpenViduRole, Recording, Session, SessionProperties } from 'openvidu-node-client';
import { OPENVIDU_URL, OPENVIDU_SECRET } from '../config';
import axios from 'axios';
export class OpenViduService {
RECORDING_TOKEN_NAME = 'ovCallRecordingToken';
ADMIN_TOKEN_NAME = 'ovCallAdminToken';
recordingMap: Map<string, { token: string; recordingId: string }> = new Map<string, { token: string; recordingId: string }>();
adminTokens: string[] = [];
protected static instance: OpenViduService;
private openvidu: OpenVidu;
private openvidu: OpenVidu;
constructor(){
this.openvidu = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET);
}
public async createSession(sessionId: string): Promise<Session> {
console.log("Creating session: ", sessionId);
let sessionProperties: SessionProperties = {customSessionId: sessionId};
return await this.openvidu.createSession(sessionProperties);
private constructor() {
this.openvidu = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET);
}
public async createConnection(session: Session, nickname: string): Promise<Connection> {
console.log(`Requesting token for session ${session.sessionId}`);
let connectionProperties: ConnectionProperties = {}
if(!!nickname) {
connectionProperties.data = JSON.stringify({ openviduCustomConnectionId: nickname });
}
console.log('Connection Properties:', connectionProperties);
return await session.createConnection(connectionProperties);
}
static getInstance() {
if (!OpenViduService.instance) {
OpenViduService.instance = new OpenViduService();
}
return OpenViduService.instance;
}
getBasicAuth(): string {
return this.openvidu.basicAuth;
}
getDateFromCookie(cookies: any): number {
const cookieToken = cookies[this.RECORDING_TOKEN_NAME];
if (!!cookieToken) {
const cookieTokenUrl = new URL(cookieToken);
const date = cookieTokenUrl?.searchParams.get('createdAt');
return Number(date);
} else {
return Date.now();
}
}
getSessionIdFromCookie(cookies: any): string {
try {
const cookieTokenUrl = new URL(cookies[this.RECORDING_TOKEN_NAME]);
return cookieTokenUrl?.searchParams.get('sessionId');
} catch (error) {
console.error(error);
return '';
}
}
isValidToken(sessionId: string, cookies: any): boolean {
try {
const storedTokenUrl = new URL(this.recordingMap.get(sessionId)?.token);
const cookieTokenUrl = new URL(cookies[this.RECORDING_TOKEN_NAME]);
if (!!cookieTokenUrl && !!storedTokenUrl) {
const cookieSessionId = cookieTokenUrl.searchParams.get('sessionId');
const cookieToken = cookieTokenUrl.searchParams.get(this.RECORDING_TOKEN_NAME);
const cookieDate = cookieTokenUrl.searchParams.get('createdAt');
const storedToken = storedTokenUrl.searchParams.get(this.RECORDING_TOKEN_NAME);
const storedDate = storedTokenUrl.searchParams.get('createdAt');
return sessionId === cookieSessionId && cookieToken === storedToken && cookieDate === storedDate;
}
return false;
} catch (error) {
return false;
}
}
public async createSession(sessionId: string): Promise<Session> {
console.log('Creating session: ', sessionId);
let sessionProperties: SessionProperties = { customSessionId: sessionId };
const session = await this.openvidu.createSession(sessionProperties);
await session.fetch();
return session;
}
public createConnection(session: Session, nickname: string, role: OpenViduRole): Promise<Connection> {
console.log(`Requesting token for session ${session.sessionId}`);
let connectionProperties: ConnectionProperties = { role };
if (!!nickname) {
connectionProperties.data = JSON.stringify({ openviduCustomConnectionId: nickname });
}
console.log('Connection Properties:', connectionProperties);
return session.createConnection(connectionProperties);
}
public async startRecording(sessionId: string): Promise<Recording> {
return this.openvidu.startRecording(sessionId);
}
public stopRecording(recordingId: string): Promise<Recording> {
return this.openvidu.stopRecording(recordingId);
}
public deleteRecording(recordingId: string): Promise<Error> {
return this.openvidu.deleteRecording(recordingId);
}
public getRecording(recordingId: string): Promise<Recording> {
return this.openvidu.getRecording(recordingId);
}
public async listAllRecordings(withB64Thumbnail: boolean = false): Promise<Recording[]> {
let recordings = await this.openvidu.listRecordings();
if (withB64Thumbnail) {
for (const rec of recordings) {
let thumbnailUrl = `${rec.url.substring(0, rec.url.lastIndexOf('/'))}/${rec.id}.jpg`;
const headers = { Authorization: this.getBasicAuth() };
let image = await axios.get(thumbnailUrl, { headers, responseType: 'arraybuffer' });
rec['thumbnailB64'] = `data:${image.headers['content-type']};base64,${Buffer.from(image.data).toString('base64')}`;
}
}
return recordings;
}
public async listRecordingsBySessionIdAndDate(sessionId: string, date: number) {
const recordingList: Recording[] = await this.listAllRecordings();
return recordingList.filter((recording) => recording.sessionId === sessionId && date <= recording.createdAt);
}
}

View File

@ -1,2 +1,15 @@
<ov-videoconference (onJoinButtonClicked)="onJoinButtonClicked()" (onToolbarLeaveButtonClicked)="onLeaveButtonClicked()" [tokens]="tokens">
<ov-videoconference
[tokens]="tokens"
[recordingActivityRecordingsList]="recordingList"
[recordingActivityRecordingError]="recordingError"
(onToolbarLeaveButtonClicked)="onLeaveButtonClicked()"
(onToolbarStartRecordingClicked)="onStartRecordingClicked()"
(onActivitiesPanelStartRecordingClicked)="onStartRecordingClicked()"
(onToolbarStopRecordingClicked)="onStopRecordingClicked()"
(onActivitiesPanelStopRecordingClicked)="onStopRecordingClicked()"
(onActivitiesPanelDownloadRecordingClicked)="onDownloadRecordingClicked($event)"
(onActivitiesPanelDeleteRecordingClicked)="onDeleteRecordingClicked($event)"
(onActivitiesPanelPlayRecordingClicked)="onPlayRecordingClicked($event)"
(onRefreshRecordingsClicked)="onRefreshRecordingsClicked()"
>
</ov-videoconference>

View File

@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { ParticipantService } from 'openvidu-angular';
import { ParticipantService, RecordingInfo, RecordingService, TokenModel } from 'openvidu-angular';
import { RestService } from '../../services/rest.service';
@ -11,14 +11,18 @@ import { RestService } from '../../services/rest.service';
})
export class CallComponent implements OnInit {
sessionId = '';
tokens: { webcam: string; screen: string };
tokens: TokenModel;
joinSessionClicked: boolean = false;
closeClicked: boolean = false;
isSessionAlive: boolean = false;
recordingList: RecordingInfo[] = [];
recordingError: any;
constructor(
private restService: RestService,
private participantService: ParticipantService,
private recordingService: RecordingService,
private router: Router,
private route: ActivatedRoute
) {}
@ -37,16 +41,56 @@ export class CallComponent implements OnInit {
nickname = this.participantService.getLocalParticipant().getNickname();
}
const response = await this.restService.getTokens(this.sessionId, nickname);
this.recordingList = response.recordings;
this.tokens = {
webcam: await this.restService.getToken(this.sessionId, nickname),
screen: await this.restService.getToken(this.sessionId, nickname)
webcam: response.cameraToken,
screen: response.screenToken
};
}
async onJoinButtonClicked() {}
onLeaveButtonClicked() {
this.isSessionAlive = false;
this.closeClicked = true;
this.router.navigate([`/`]);
}
async onStartRecordingClicked() {
try {
await this.restService.startRecording(this.sessionId);
} catch (error) {
this.recordingError = error;
}
}
async onStopRecordingClicked() {
try {
this.recordingList = await this.restService.stopRecording(this.sessionId);
} catch (error) {
this.recordingError = error;
}
}
async onDeleteRecordingClicked(recordingId: string) {
try {
this.recordingList = await this.restService.deleteRecording(recordingId);
} catch (error) {
this.recordingError = error;
}
}
async onDownloadRecordingClicked(recordingId: string) {
try {
const file = await this.restService.downloadRecording(recordingId);
this.recordingService.downloadRecording(recordingId, file);
} catch (error) {
this.recordingError = error;
}
}
async onPlayRecordingClicked(recordingId: string) {
try {
const recording: Blob = await this.restService.downloadRecording(recordingId);
this.recordingService.playRecording(recording);
} catch (error) {
this.recordingError = error;
}
}
}

View File

@ -1,6 +1,7 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { catchError, lastValueFrom } from 'rxjs';
import { catchError, lastValueFrom, Observable } from 'rxjs';
import { RecordingInfo } from 'openvidu-angular';
@Injectable({
providedIn: 'root'
@ -11,9 +12,42 @@ export class RestService {
constructor(private http: HttpClient) {
this.baseHref = '/' + (!!window.location.pathname.split('/')[1] ? window.location.pathname.split('/')[1] + '/' : '');
}
async getToken(sessionId: string, nickname?: string): Promise<string> {
async getTokens(
sessionId: string,
nickname?: string
): Promise<{ cameraToken: string; screenToken: string; recordings?: RecordingInfo[] }> {
return this.postRequest('sessions', { sessionId, nickname });
}
login(password: string): Promise<any[]> {
return this.postRequest('admin/login', { password });
}
logout(): Promise<void> {
return this.postRequest('admin/logout', {});
}
getRecordings() {
return this.getRequest(`recordings/`);
}
startRecording(sessionId: string) {
return this.postRequest('recordings/start', { sessionId });
}
stopRecording(sessionId: string) {
return this.postRequest('recordings/stop', { sessionId });
}
downloadRecording(recordingId: string): Promise<Blob> {
return this.getRequest(`recordings/${recordingId}`, 'blob');
}
deleteRecording(recordingId: string): Promise<RecordingInfo[]> {
return this.deleteRequest(`recordings/delete/${recordingId}`);
}
private postRequest(path: string, body: any): any {
try {
return lastValueFrom(this.http.post<any>(this.baseHref + 'call', { sessionId, nickname }));
return lastValueFrom(this.http.post<any>(this.baseHref + path, body));
} catch (error) {
if (error.status === 404) {
throw { status: error.status, message: 'Cannot connect with backend. ' + error.url + ' not found' };
@ -21,4 +55,31 @@ export class RestService {
throw error;
}
}
private getRequest(path: string, responseType?: string): any {
try {
const options = {};
if (responseType) {
options['responseType'] = responseType;
}
return lastValueFrom(this.http.get(`${this.baseHref}${path}`, options));
} catch (error) {
if (error.status === 404) {
throw { status: error.status, message: 'Cannot connect with backend. ' + error.url + ' not found' };
}
throw error;
}
}
private deleteRequest(path: string) {
try {
return lastValueFrom(this.http.delete<any>(`${this.baseHref}${path}`));
} catch (error) {
console.log(error);
if (error.status === 404) {
throw { status: error.status, message: 'Cannot connect with backend. ' + error.url + ' not found' };
}
throw error;
}
}
}

View File

@ -5,7 +5,6 @@
<title>OpenviduCall</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet" />