mirror of https://github.com/OpenVidu/openvidu.git
openvidu-testapp/openvidu-test-e2e: individual stream recording
parent
e752685b31
commit
0ba9bbb152
|
@ -22,10 +22,15 @@ import static org.openqa.selenium.OutputType.BASE64;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -35,6 +40,8 @@ import java.util.Queue;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
@ -170,9 +177,7 @@ public class OpenViduTestAppE2eTest {
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
void dispose() {
|
void dispose() {
|
||||||
if (user != null) {
|
user.dispose();
|
||||||
user.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -974,9 +979,9 @@ public class OpenViduTestAppE2eTest {
|
||||||
void remoteComposedRecordTest() throws Exception {
|
void remoteComposedRecordTest() throws Exception {
|
||||||
setupBrowser("chrome");
|
setupBrowser("chrome");
|
||||||
|
|
||||||
log.info("Remote record");
|
log.info("Remote composed record");
|
||||||
|
|
||||||
final String sessionName = "RECORDED_SESSION";
|
final String sessionName = "COMPOSED_RECORDED_SESSION";
|
||||||
|
|
||||||
user.getDriver().findElement(By.id("add-user-btn")).click();
|
user.getDriver().findElement(By.id("add-user-btn")).click();
|
||||||
user.getDriver().findElement(By.id("session-name-input-0")).clear();
|
user.getDriver().findElement(By.id("session-name-input-0")).clear();
|
||||||
|
@ -1073,9 +1078,9 @@ public class OpenViduTestAppE2eTest {
|
||||||
File file2 = new File(recordingsPath + sessionName + "/" + ".recording." + sessionName);
|
File file2 = new File(recordingsPath + sessionName + "/" + ".recording." + sessionName);
|
||||||
File file3 = new File(recordingsPath + sessionName + "/" + sessionName + ".jpg");
|
File file3 = new File(recordingsPath + sessionName + "/" + sessionName + ".jpg");
|
||||||
|
|
||||||
Assert.assertTrue(file1.exists() || file1.length() > 0);
|
Assert.assertTrue(file1.exists() && file1.length() > 0);
|
||||||
Assert.assertTrue(file2.exists() || file2.length() > 0);
|
Assert.assertTrue(file2.exists() && file2.length() > 0);
|
||||||
Assert.assertTrue(file3.exists() || file3.length() > 0);
|
Assert.assertTrue(file3.exists() && file3.length() > 0);
|
||||||
|
|
||||||
Assert.assertTrue(
|
Assert.assertTrue(
|
||||||
this.recordedFileFine(file1, new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(sessionName)));
|
this.recordedFileFine(file1, new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(sessionName)));
|
||||||
|
@ -1109,7 +1114,117 @@ public class OpenViduTestAppE2eTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Remote individual record")
|
@DisplayName("Remote individual record")
|
||||||
void remoteIndividualRecordTest() throws Exception {
|
void remoteIndividualRecordTest() throws Exception {
|
||||||
|
setupBrowser("chrome");
|
||||||
|
|
||||||
|
log.info("Remote individual record");
|
||||||
|
|
||||||
|
final String sessionName = "TestSession";
|
||||||
|
final String recordingName = "CUSTOM_NAME";
|
||||||
|
|
||||||
|
user.getDriver().findElement(By.id("add-user-btn")).click();
|
||||||
|
user.getDriver().findElement(By.id("session-name-input-0")).clear();
|
||||||
|
user.getDriver().findElement(By.id("session-name-input-0")).sendKeys(sessionName);
|
||||||
|
|
||||||
|
user.getDriver().findElement(By.id("auto-join-checkbox")).click();
|
||||||
|
user.getDriver().findElement(By.id("one2one-btn")).click();
|
||||||
|
|
||||||
|
user.getEventManager().waitUntilEventReaches("connectionCreated", 4);
|
||||||
|
user.getEventManager().waitUntilEventReaches("accessAllowed", 2);
|
||||||
|
user.getEventManager().waitUntilEventReaches("streamCreated", 4);
|
||||||
|
user.getEventManager().waitUntilEventReaches("streamPlaying", 4);
|
||||||
|
|
||||||
|
Assert.assertTrue(user.getEventManager().assertMediaTracks(user.getDriver().findElements(By.tagName("video")),
|
||||||
|
true, true));
|
||||||
|
|
||||||
|
user.getDriver().findElement(By.id("session-api-btn-0")).click();
|
||||||
|
Thread.sleep(1000);
|
||||||
|
user.getDriver().findElement(By.id("rec-properties-btn")).click();
|
||||||
|
Thread.sleep(500);
|
||||||
|
|
||||||
|
// Set recording name
|
||||||
|
user.getDriver().findElement(By.id("recording-name-field")).sendKeys(recordingName);
|
||||||
|
// Set OutputMode to INDIVIDUAL
|
||||||
|
user.getDriver().findElement(By.id("rec-outputmode-select")).click();
|
||||||
|
Thread.sleep(500);
|
||||||
|
user.getDriver().findElement(By.id("option-INDIVIDUAL")).click();
|
||||||
|
Thread.sleep(500);
|
||||||
|
|
||||||
|
user.getDriver().findElement(By.id("start-recording-btn")).click();
|
||||||
|
|
||||||
|
user.getWaiter().until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value",
|
||||||
|
"Recording started [" + sessionName + "]"));
|
||||||
|
|
||||||
|
user.getEventManager().waitUntilEventReaches("recordingStarted", 2);
|
||||||
|
|
||||||
|
Thread.sleep(8000);
|
||||||
|
|
||||||
|
user.getDriver().findElement(By.id("recording-id-field")).clear();
|
||||||
|
user.getDriver().findElement(By.id("recording-id-field")).sendKeys(sessionName);
|
||||||
|
|
||||||
|
// Try to start an ongoing recording
|
||||||
|
user.getDriver().findElement(By.id("start-recording-btn")).click();
|
||||||
|
user.getWaiter()
|
||||||
|
.until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value", "Error [409]"));
|
||||||
|
|
||||||
|
// Try to get an existing recording
|
||||||
|
user.getDriver().findElement(By.id("get-recording-btn")).click();
|
||||||
|
user.getWaiter().until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value",
|
||||||
|
"Recording got [" + sessionName + "]"));
|
||||||
|
|
||||||
|
// Try to delete an ongoing recording
|
||||||
|
user.getDriver().findElement(By.id("delete-recording-btn")).click();
|
||||||
|
user.getWaiter()
|
||||||
|
.until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value", "Error [409]"));
|
||||||
|
|
||||||
|
// List existing recordings (one)
|
||||||
|
user.getDriver().findElement(By.id("list-recording-btn")).click();
|
||||||
|
user.getWaiter().until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value",
|
||||||
|
"Recording list [" + sessionName + "]"));
|
||||||
|
|
||||||
|
// Stop ongoing recording
|
||||||
|
user.getDriver().findElement(By.id("stop-recording-btn")).click();
|
||||||
|
user.getWaiter().until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value",
|
||||||
|
"Recording stopped [" + sessionName + "]"));
|
||||||
|
|
||||||
|
user.getEventManager().waitUntilEventReaches("recordingStopped", 2);
|
||||||
|
|
||||||
|
String recordingsPath = "/opt/openvidu/recordings/";
|
||||||
|
|
||||||
|
// Should be only 2 files: zip and metadata
|
||||||
|
File folder = new File(recordingsPath + sessionName);
|
||||||
|
Assert.assertEquals(folder.listFiles().length, 2);
|
||||||
|
|
||||||
|
File file1 = new File(recordingsPath + sessionName + "/" + recordingName + ".zip");
|
||||||
|
File file2 = new File(recordingsPath + sessionName + "/" + ".recording." + sessionName);
|
||||||
|
|
||||||
|
Assert.assertTrue(file1.exists() && file1.length() > 0);
|
||||||
|
Assert.assertTrue(file2.exists() && file2.length() > 0);
|
||||||
|
|
||||||
|
unzipAndCheckFile(recordingsPath + sessionName + "/", recordingName + ".zip",
|
||||||
|
new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(sessionName));
|
||||||
|
|
||||||
|
// Try to get the stopped recording
|
||||||
|
user.getDriver().findElement(By.id("get-recording-btn")).click();
|
||||||
|
user.getWaiter().until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value",
|
||||||
|
"Recording got [" + sessionName + "]"));
|
||||||
|
|
||||||
|
// Try to list the stopped recording
|
||||||
|
user.getDriver().findElement(By.id("list-recording-btn")).click();
|
||||||
|
user.getWaiter().until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value",
|
||||||
|
"Recording list [" + sessionName + "]"));
|
||||||
|
|
||||||
|
// Delete the recording
|
||||||
|
user.getDriver().findElement(By.id("delete-recording-btn")).click();
|
||||||
|
user.getWaiter()
|
||||||
|
.until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value", "Recording deleted"));
|
||||||
|
|
||||||
|
Assert.assertFalse(file1.exists());
|
||||||
|
Assert.assertFalse(file2.exists());
|
||||||
|
|
||||||
|
user.getDriver().findElement(By.id("close-dialog-btn")).click();
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
gracefullyLeaveParticipants(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1553,6 +1668,7 @@ public class OpenViduTestAppE2eTest {
|
||||||
// Get a frame at 75% duration
|
// Get a frame at 75% duration
|
||||||
frame = FrameGrab.getFrameAtSec(file, (double) (recording.getDuration() * 0.75));
|
frame = FrameGrab.getFrameAtSec(file, (double) (recording.getDuration() * 0.75));
|
||||||
Map<String, Long> colorMap = this.averageColor(AWTUtil.toBufferedImage(frame));
|
Map<String, Long> colorMap = this.averageColor(AWTUtil.toBufferedImage(frame));
|
||||||
|
|
||||||
log.info("Recording map color: {}", colorMap.toString());
|
log.info("Recording map color: {}", colorMap.toString());
|
||||||
isFine = this.checkVideoAverageRgbGreen(colorMap);
|
isFine = this.checkVideoAverageRgbGreen(colorMap);
|
||||||
} catch (IOException | JCodecException e) {
|
} catch (IOException | JCodecException e) {
|
||||||
|
@ -1602,4 +1718,45 @@ public class OpenViduTestAppE2eTest {
|
||||||
return colorMap;
|
return colorMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void unzipAndCheckFile(String path, String fileName, Recording recording) {
|
||||||
|
final int BUFFER = 2048;
|
||||||
|
final List<String> recordingFiles = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
BufferedOutputStream dest = null;
|
||||||
|
FileInputStream fis = new FileInputStream(path + fileName);
|
||||||
|
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
|
||||||
|
ZipEntry entry;
|
||||||
|
while ((entry = zis.getNextEntry()) != null) {
|
||||||
|
log.info("Extracting: " + entry);
|
||||||
|
if (entry.getName().endsWith(".webm")) {
|
||||||
|
recordingFiles.add(entry.getName());
|
||||||
|
}
|
||||||
|
int count;
|
||||||
|
byte data[] = new byte[BUFFER];
|
||||||
|
FileOutputStream fos = new FileOutputStream(path + entry.getName());
|
||||||
|
dest = new BufferedOutputStream(fos, BUFFER);
|
||||||
|
while ((count = zis.read(data, 0, BUFFER)) != -1) {
|
||||||
|
dest.write(data, 0, count);
|
||||||
|
}
|
||||||
|
dest.flush();
|
||||||
|
dest.close();
|
||||||
|
}
|
||||||
|
zis.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
long totalFileSize = 0;
|
||||||
|
for (String videoFileName : recordingFiles) {
|
||||||
|
File videoFile = new File(path + videoFileName);
|
||||||
|
totalFileSize += videoFile.length();
|
||||||
|
Assert.assertTrue(videoFile.exists() && videoFile.length() > 0);
|
||||||
|
videoFile.delete();
|
||||||
|
}
|
||||||
|
Assert.assertEquals(recording.getSize() / 1000, totalFileSize / 1000);
|
||||||
|
|
||||||
|
File jsonSyncFile = new File(path + recording.getSessionId() + ".json");
|
||||||
|
Assert.assertTrue(jsonSyncFile.exists() && jsonSyncFile.length() > 0);
|
||||||
|
jsonSyncFile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
#response-text-area {
|
||||||
|
width: 100%;
|
||||||
|
color: #808080;
|
||||||
|
}
|
||||||
|
|
||||||
|
#response-text-area textarea {
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-dialog-content button,
|
||||||
|
mat-divider {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-dialog-content button {
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(0, 0, 0, 0.54);
|
||||||
|
font-weight: 400;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
margin-top: 13px
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner-text-input {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#rec-properties-btn {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#rec-properties-div {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid #00000026;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#rec-properties-div mat-form-field {
|
||||||
|
max-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#rec-properties-div mat-checkbox {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
<div>
|
||||||
|
<h2 mat-dialog-title>API REST</h2>
|
||||||
|
<mat-dialog-content>
|
||||||
|
<label class="label">Sessions</label>
|
||||||
|
<div>
|
||||||
|
<button mat-button id="get-session-btn" (click)="fetchActiveConnections()">Fetch</button>
|
||||||
|
<button mat-button id="list-sessions-btn" (click)="fetchActiveSessions()">Fetch all</button>
|
||||||
|
<button mat-button id="close-session-btn" (click)="closeSession()">Close this session</button>
|
||||||
|
</div>
|
||||||
|
<mat-form-field class="inner-text-input">
|
||||||
|
<input matInput id="resource-id-field" placeholder="resourceId" [(ngModel)]="resourceId">
|
||||||
|
</mat-form-field>
|
||||||
|
<div>
|
||||||
|
<button mat-button id="force-disconnect-api-btn" (click)="forceDisconnect()" [disabled]="!resourceId">Force
|
||||||
|
disconnect</button>
|
||||||
|
<button mat-button id="force-unpublish-api-btn" (click)="forceUnpublish()" [disabled]="!resourceId">Force
|
||||||
|
unpublish</button>
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
</div>
|
||||||
|
<label class="label">Recordings</label>
|
||||||
|
<button mat-button id="list-recording-btn" (click)="listRecordings()">List recordings</button>
|
||||||
|
<button mat-button id="start-recording-btn" (click)="startRecording()">Start recording</button>
|
||||||
|
<button id="rec-properties-btn" mat-icon-button style="width: 24px; height: 24px; line-height: 24px;" title="Recording properties"
|
||||||
|
(click)="toggleRecProperties()">
|
||||||
|
<mat-icon style="font-size: 18px; line-height: 18px; width: 18px; height: 18px" aria-label="Recording properties">{{recPropertiesIcon}}</mat-icon>
|
||||||
|
</button>
|
||||||
|
<div *ngIf="showRecProperties" id="rec-properties-div">
|
||||||
|
<div>
|
||||||
|
<mat-form-field class="inner-text-input">
|
||||||
|
<input matInput id="recording-name-field" placeholder="Recording name" [(ngModel)]="recordingProperties.name">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field class="inner-text-input">
|
||||||
|
<mat-select id="rec-outputmode-select" placeholder="Output mode" [(ngModel)]="recordingProperties.outputMode">
|
||||||
|
<mat-option *ngFor="let enumerator of enumToArray(recMode)" [value]="enumerator">
|
||||||
|
<span [attr.id]="'option-' + enumerator">{{ enumerator }}</span>
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="recordingProperties.outputMode.toString() === recMode[recMode.COMPOSED]" id="rec-layout-div">
|
||||||
|
<mat-form-field class="inner-text-input">
|
||||||
|
<mat-select placeholder="Recording layout" [(ngModel)]="recordingProperties.recordingLayout">
|
||||||
|
<mat-option *ngFor="let enumerator of enumToArray(recLayouts)" [value]="enumerator">
|
||||||
|
{{ enumerator }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field *ngIf="recordingProperties.recordingLayout.toString() === recLayouts[recLayouts.CUSTOM]" class="inner-text-input">
|
||||||
|
<input matInput placeholder="Custom layout" type="text" [(ngModel)]="recordingProperties.customLayout">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-checkbox [(ngModel)]="recordingProperties.hasAudio">Has audio</mat-checkbox>
|
||||||
|
<mat-checkbox [(ngModel)]="recordingProperties.hasVideo">Has video</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<mat-form-field class="inner-text-input">
|
||||||
|
<input matInput id="recording-id-field" placeholder="recordingId" [(ngModel)]="recordingId">
|
||||||
|
</mat-form-field>
|
||||||
|
<div>
|
||||||
|
<button mat-button id="stop-recording-btn" (click)="stopRecording()" [disabled]="!recordingId">Stop recording</button>
|
||||||
|
<button mat-button id="get-recording-btn" (click)="getRecording()" [disabled]="!recordingId">Get recording</button>
|
||||||
|
<button mat-button id="delete-recording-btn" (click)="deleteRecording()" [disabled]="!recordingId">Delete
|
||||||
|
recording</button>
|
||||||
|
</div>
|
||||||
|
<mat-form-field *ngIf="!!response" id="response-text-area" appearance="fill">
|
||||||
|
<textarea id="api-response-text-area" [(ngModel)]="response" matInput readonly></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
</mat-dialog-content>
|
||||||
|
<mat-dialog-actions>
|
||||||
|
<button mat-button id="close-dialog-btn" [mat-dialog-close]="{session: session, recordingProperties: recordingProperties}">CLOSE</button>
|
||||||
|
</mat-dialog-actions>
|
||||||
|
</div>
|
|
@ -1,50 +1,12 @@
|
||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
|
||||||
|
|
||||||
import { OpenVidu as OpenViduAPI, Session as SessionAPI } from 'openvidu-node-client';
|
import { OpenVidu as OpenViduAPI, Session as SessionAPI, Recording, RecordingProperties, RecordingLayout } from 'openvidu-node-client';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-session-api-dialog',
|
selector: 'app-session-api-dialog',
|
||||||
template: `
|
templateUrl: './session-api-dialog.component.html',
|
||||||
<div>
|
styleUrls: ['./session-api-dialog.component.css']
|
||||||
<h2 mat-dialog-title>API REST</h2>
|
|
||||||
<mat-dialog-content>
|
|
||||||
<label class="label">Sessions</label>
|
|
||||||
<button mat-button id="get-session-btn" (click)="fetchActiveConnections()">Fetch</button>
|
|
||||||
<button mat-button id="list-sessions-btn" (click)="fetchActiveSessions()">Fetch all</button>
|
|
||||||
<button mat-button id="close-session-btn" (click)="closeSession()">Close this session</button>
|
|
||||||
<mat-form-field class="inner-text-input">
|
|
||||||
<input matInput id="resource-id-field" placeholder="resourceId" [(ngModel)]="resourceId">
|
|
||||||
</mat-form-field>
|
|
||||||
<button mat-button id="force-disconnect-api-btn" (click)="forceDisconnect()" [disabled]="!resourceId">Force disconnect</button>
|
|
||||||
<button mat-button id="force-unpublish-api-btn" (click)="forceUnpublish()" [disabled]="!resourceId">Force unpublish</button>
|
|
||||||
<mat-divider></mat-divider>
|
|
||||||
<label class="label">Recordings</label>
|
|
||||||
<button mat-button id="start-recording-btn" (click)="startRecording()">Start recording</button>
|
|
||||||
<button mat-button id="list-recording-btn" (click)="listRecordings()">List recordings</button>
|
|
||||||
<mat-form-field class="inner-text-input">
|
|
||||||
<input matInput id="recording-id-field" placeholder="recordingId" [(ngModel)]="recordingId">
|
|
||||||
</mat-form-field>
|
|
||||||
<button mat-button id="stop-recording-btn" (click)="stopRecording()" [disabled]="!recordingId">Stop recording</button>
|
|
||||||
<button mat-button id="get-recording-btn" (click)="getRecording()" [disabled]="!recordingId">Get recording</button>
|
|
||||||
<button mat-button id="delete-recording-btn" (click)="deleteRecording()" [disabled]="!recordingId">Delete recording</button>
|
|
||||||
<mat-form-field *ngIf="!!response" id="response-text-area" appearance="fill">
|
|
||||||
<textarea id="api-response-text-area" [(ngModel)]="response" matInput readonly></textarea>
|
|
||||||
</mat-form-field>
|
|
||||||
</mat-dialog-content>
|
|
||||||
<mat-dialog-actions>
|
|
||||||
<button mat-button id="close-dialog-btn" [mat-dialog-close]="{session: session}">CLOSE</button>
|
|
||||||
</mat-dialog-actions>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
styles: [
|
|
||||||
'#response-text-area { width: 100%; color: #808080; }',
|
|
||||||
'#response-text-area textarea { resize: none; }',
|
|
||||||
'mat-dialog-content button, mat-divider { margin-bottom: 5px; }',
|
|
||||||
'mat-dialog-content button { height: 30px; line-height: 30px; padding-left: 12px; padding-right: 12px; display: inline-flex;}',
|
|
||||||
'.label { display: block; font-size: 12px; color: rgba(0, 0, 0, 0.54); font-weight: 400; margin-bottom: 5px; margin-top: 13px}',
|
|
||||||
'.inner-text-input { margin-left: 16px; }'
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
export class SessionApiDialogComponent {
|
export class SessionApiDialogComponent {
|
||||||
|
|
||||||
|
@ -55,11 +17,19 @@ export class SessionApiDialogComponent {
|
||||||
resourceId: string;
|
resourceId: string;
|
||||||
response: string;
|
response: string;
|
||||||
|
|
||||||
|
recordingProperties: RecordingProperties;
|
||||||
|
recMode = Recording.OutputMode;
|
||||||
|
recLayouts = RecordingLayout;
|
||||||
|
customLayout = '';
|
||||||
|
recPropertiesIcon = 'add_circle';
|
||||||
|
showRecProperties = false;
|
||||||
|
|
||||||
constructor(public dialogRef: MatDialogRef<SessionApiDialogComponent>,
|
constructor(public dialogRef: MatDialogRef<SessionApiDialogComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data) {
|
@Inject(MAT_DIALOG_DATA) public data) {
|
||||||
this.OV = data.openVidu;
|
this.OV = data.openVidu;
|
||||||
this.session = data.session;
|
this.session = data.session;
|
||||||
this.sessionId = data.sessionId;
|
this.sessionId = data.sessionId;
|
||||||
|
this.recordingProperties = data.recordingProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
closeSession() {
|
closeSession() {
|
||||||
|
@ -80,7 +50,7 @@ export class SessionApiDialogComponent {
|
||||||
|
|
||||||
startRecording() {
|
startRecording() {
|
||||||
console.log('Starting recording');
|
console.log('Starting recording');
|
||||||
this.OV.startRecording(this.sessionId)
|
this.OV.startRecording(this.sessionId, this.recordingProperties)
|
||||||
.then(recording => {
|
.then(recording => {
|
||||||
this.response = 'Recording started [' + recording.id + ']';
|
this.response = 'Recording started [' + recording.id + ']';
|
||||||
})
|
})
|
||||||
|
@ -195,4 +165,13 @@ export class SessionApiDialogComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enumToArray(enumerator: any) {
|
||||||
|
return Object.keys(enumerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleRecProperties() {
|
||||||
|
this.showRecProperties = !this.showRecProperties;
|
||||||
|
this.recPropertiesIcon = this.showRecProperties ? 'remove_circle' : 'add_circle';
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,9 @@ import {
|
||||||
RecordingMode,
|
RecordingMode,
|
||||||
RecordingLayout,
|
RecordingLayout,
|
||||||
TokenOptions,
|
TokenOptions,
|
||||||
OpenViduRole
|
OpenViduRole,
|
||||||
|
RecordingProperties,
|
||||||
|
Recording
|
||||||
} from 'openvidu-node-client';
|
} from 'openvidu-node-client';
|
||||||
import { MatDialog, MAT_CHECKBOX_CLICK_ACTION } from '@angular/material';
|
import { MatDialog, MAT_CHECKBOX_CLICK_ACTION } from '@angular/material';
|
||||||
import { ExtensionDialogComponent } from '../dialogs/extension-dialog/extension-dialog.component';
|
import { ExtensionDialogComponent } from '../dialogs/extension-dialog/extension-dialog.component';
|
||||||
|
@ -71,6 +73,9 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
subscribeToRemote = false;
|
subscribeToRemote = false;
|
||||||
optionsVideo = 'video';
|
optionsVideo = 'video';
|
||||||
|
|
||||||
|
// Recording options
|
||||||
|
recordingProperties: RecordingProperties;
|
||||||
|
|
||||||
// OpenVidu Browser objects
|
// OpenVidu Browser objects
|
||||||
OV: OpenVidu;
|
OV: OpenVidu;
|
||||||
session: Session;
|
session: Session;
|
||||||
|
@ -441,7 +446,7 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
this.publisher = this.OV.initPublisher(
|
this.publisher = this.OV.initPublisher(
|
||||||
undefined,
|
undefined,
|
||||||
this.publisherProperties,
|
this.publisherProperties,
|
||||||
(err) => {
|
err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.warn(err);
|
console.warn(err);
|
||||||
this.openviduError = err;
|
this.openviduError = err;
|
||||||
|
@ -528,7 +533,7 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
width: '450px'
|
width: '450px'
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((result) => {
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
if (!!result) {
|
if (!!result) {
|
||||||
this.sessionProperties = result.sessionProperties;
|
this.sessionProperties = result.sessionProperties;
|
||||||
if (!!this.sessionProperties.customSessionId) {
|
if (!!this.sessionProperties.customSessionId) {
|
||||||
|
@ -546,11 +551,20 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
openSessionApiDialog() {
|
openSessionApiDialog() {
|
||||||
const dialogRef = this.dialog.open(SessionApiDialogComponent, {
|
const dialogRef = this.dialog.open(SessionApiDialogComponent, {
|
||||||
data: {
|
data: {
|
||||||
openVidu: this.OV_NodeClient ? this.OV_NodeClient : new OpenViduAPI(this.openviduUrl, this.openviduSecret),
|
openVidu: !!this.OV_NodeClient ? this.OV_NodeClient : new OpenViduAPI(this.openviduUrl, this.openviduSecret),
|
||||||
session: this.sessionAPI,
|
session: this.sessionAPI,
|
||||||
sessionId: !!this.session ? this.session.sessionId : this.sessionName
|
sessionId: !!this.session ? this.session.sessionId : this.sessionName,
|
||||||
|
recordingProperties: !!this.recordingProperties ? this.recordingProperties :
|
||||||
|
{
|
||||||
|
name: '',
|
||||||
|
outputMode: Recording.OutputMode.COMPOSED,
|
||||||
|
recordingLayout: this.sessionProperties.defaultRecordingLayout,
|
||||||
|
customLayout: this.sessionProperties.defaultCustomLayout,
|
||||||
|
hasAudio: true,
|
||||||
|
hasVideo: true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
width: '280px',
|
width: '425px',
|
||||||
disableClose: true
|
disableClose: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -558,6 +572,7 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
if (!result.session) {
|
if (!result.session) {
|
||||||
delete this.sessionAPI;
|
delete this.sessionAPI;
|
||||||
}
|
}
|
||||||
|
this.recordingProperties = result.recordingProperties;
|
||||||
document.getElementById('session-api-btn-' + this.index).classList.remove('cdk-program-focused');
|
document.getElementById('session-api-btn-' + this.index).classList.remove('cdk-program-focused');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -588,7 +603,7 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
disableClose: true
|
disableClose: true
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((result) => {
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
|
|
||||||
if (!!this.session && JSON.stringify(this.sessionEvents) !== JSON.stringify(oldValues)) {
|
if (!!this.session && JSON.stringify(this.sessionEvents) !== JSON.stringify(oldValues)) {
|
||||||
this.updateSessionEvents(oldValues, false);
|
this.updateSessionEvents(oldValues, false);
|
||||||
|
|
Loading…
Reference in New Issue