Add failed status to recordings

Although recordings should not fail in some cases they could (for
example, if the configuration is wrong, the disk is full...). A new
recording status, "failed", has been added to notify that situation to
the Nextcloud server from the recording server.

Typically call participants will not be able to do anything about the
failure, so the clients can just show a notification to the moderators
and hint about asking the system administrators.

In any case, even if the recording status is "failed" further recordings
can be started, which will behave as if the recording was started from
the "none" status.

Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
This commit is contained in:
Daniel Calviño Sánchez 2023-02-21 02:13:25 +01:00
parent 808c9f0e36
commit eca3161d24
15 changed files with 355 additions and 8 deletions

View file

@ -113,6 +113,7 @@ title: Constants
* `2` - Recording audio
* `3` - Starting video recording
* `4` - Starting audio recording
* `5` - Recording failed
## Chat

View file

@ -178,3 +178,20 @@
* Response:
- (Additional) Status code:
+ `404 Not Found`: When the room is not found.
### Failed call recording
* Data format:
```json
{
"type": "failed",
"failed": {
"token": "the-token-of-the-room",
},
}
```
* Response:
- (Additional) Status code:
+ `404 Not Found`: When the room is not found.

View file

@ -150,7 +150,7 @@ class Listener {
return false;
}
if ($room->getCallRecording() !== Room::RECORDING_NONE) {
if ($room->getCallRecording() !== Room::RECORDING_NONE && $room->getCallRecording() !== Room::RECORDING_FAILED) {
$this->recordingService->stop($room, $actor);
}
if ($actor instanceof Participant) {

View file

@ -544,6 +544,8 @@ class SystemMessage {
if ($currentUserIsActor) {
$parsedMessage = $this->l->t('You stopped the audio recording');
}
} elseif ($message === 'recording_failed') {
$parsedMessage = $this->l->t('The recording failed');
} elseif ($message === 'poll_voted') {
$parsedParameters['poll'] = $parameters['poll'];
$parsedParameters['poll']['id'] = (string) $parsedParameters['poll']['id'];

View file

@ -501,12 +501,14 @@ class Listener implements IEventListener {
}
public static function setCallRecording(ModifyRoomEvent $event): void {
$recordingHasStarted = in_array($event->getOldValue(), [Room::RECORDING_NONE, Room::RECORDING_VIDEO_STARTING, Room::RECORDING_AUDIO_STARTING])
$recordingHasStarted = in_array($event->getOldValue(), [Room::RECORDING_NONE, Room::RECORDING_VIDEO_STARTING, Room::RECORDING_AUDIO_STARTING, Room::RECORDING_FAILED])
&& in_array($event->getNewValue(), [Room::RECORDING_VIDEO, Room::RECORDING_AUDIO]);
$recordingHasStopped = in_array($event->getOldValue(), [Room::RECORDING_VIDEO, Room::RECORDING_AUDIO])
&& $event->getNewValue() === Room::RECORDING_NONE;
$recordingHasFailed = in_array($event->getOldValue(), [Room::RECORDING_VIDEO, Room::RECORDING_AUDIO])
&& $event->getNewValue() === Room::RECORDING_FAILED;
if (!$recordingHasStarted && !$recordingHasStopped) {
if (!$recordingHasStarted && !$recordingHasStopped && !$recordingHasFailed) {
return;
}
@ -524,15 +526,20 @@ class Listener implements IEventListener {
Room::RECORDING_VIDEO,
Room::RECORDING_AUDIO,
];
$suffix = in_array($newStatus, $startStatus) ? 'started' : 'stopped';
return $suffix;
if (in_array($newStatus, $startStatus)) {
return 'started';
}
if ($newStatus === Room::RECORDING_FAILED) {
return 'failed';
}
return 'stopped';
}
private static function getCallRecordingPrefix(ModifyRoomEvent $event): string {
$newValue = $event->getNewValue();
$oldValue = $event->getOldValue();
$isAudioStatus = $newValue === Room::RECORDING_AUDIO
|| $oldValue === Room::RECORDING_AUDIO;
|| ($oldValue === Room::RECORDING_AUDIO && $newValue !== Room::RECORDING_FAILED);
return $isAudioStatus ? 'audio_' : '';
}

View file

@ -156,6 +156,8 @@ class RecordingController extends AEnvironmentAwareController {
return $this->backendStarted($message['started']);
case 'stopped':
return $this->backendStopped($message['stopped']);
case 'failed':
return $this->backendFailed($message['failed']);
default:
return new DataResponse([
'type' => 'error',
@ -237,6 +239,30 @@ class RecordingController extends AEnvironmentAwareController {
return new DataResponse();
}
private function backendFailed(array $failed): DataResponse {
$token = $failed['token'];
try {
$room = $this->manager->getRoomByToken($token);
} catch (RoomNotFoundException $e) {
$this->logger->debug('Failed to get room {token}', [
'token' => $token,
'app' => 'spreed-recording',
]);
return new DataResponse([
'type' => 'error',
'error' => [
'code' => 'no_such_room',
'message' => 'Room not found.',
],
], Http::STATUS_NOT_FOUND);
}
$this->roomService->setCallRecording($room, Room::RECORDING_FAILED);
return new DataResponse();
}
/**
* @NoAdminRequired
* @RequireLoggedInModeratorParticipant

View file

@ -61,6 +61,7 @@ class Room {
public const RECORDING_AUDIO = 2;
public const RECORDING_VIDEO_STARTING = 3;
public const RECORDING_AUDIO_STARTING = 4;
public const RECORDING_FAILED = 5;
/** @deprecated Use self::TYPE_UNKNOWN */
public const UNKNOWN_CALL = self::TYPE_UNKNOWN;

View file

@ -75,7 +75,7 @@ class RecordingService {
if (!in_array($status, $availableRecordingTypes)) {
throw new InvalidArgumentException('status');
}
if ($room->getCallRecording() !== Room::RECORDING_NONE) {
if ($room->getCallRecording() !== Room::RECORDING_NONE && $room->getCallRecording() !== Room::RECORDING_FAILED) {
throw new InvalidArgumentException('recording');
}
if (!$room->getActiveSince() instanceof \DateTimeInterface) {

View file

@ -379,7 +379,7 @@ class RoomService {
throw new InvalidArgumentException('config');
}
$availableRecordingStatus = [Room::RECORDING_NONE, Room::RECORDING_VIDEO, Room::RECORDING_AUDIO, Room::RECORDING_VIDEO_STARTING, Room::RECORDING_AUDIO_STARTING];
$availableRecordingStatus = [Room::RECORDING_NONE, Room::RECORDING_VIDEO, Room::RECORDING_AUDIO, Room::RECORDING_VIDEO_STARTING, Room::RECORDING_AUDIO_STARTING, Room::RECORDING_FAILED];
if (!in_array($status, $availableRecordingStatus)) {
throw new InvalidArgumentException('status');
}

View file

@ -154,6 +154,23 @@ def stopped(backend, token, actorType, actorId):
backendRequest(backend, data)
def failed(backend, token):
"""
Notifies the backend that the recording failed.
:param backend: the backend of the conversation.
:param token: the token of the conversation.
"""
data = {
'type': 'failed',
'failed': {
'token': token,
},
}
backendRequest(backend, data)
def uploadRecording(backend, token, fileName, owner):
"""
Upload the recording specified by fileName.

View file

@ -250,6 +250,11 @@ class Service:
except Exception as exception:
self._stopHelpers()
try:
BackendNotifier.failed(self.backend, self.token)
except:
pass
raise
def stop(self, actorType, actorId):

View file

@ -153,6 +153,7 @@ class FeatureContext implements Context, SnippetAcceptingContext {
self::$tokenToIdentifier = [];
self::$sessionIdToUser = [
'cli' => 'cli',
'failed-to-get-session' => 'failed-to-get-session',
];
self::$userToSessionId = [];
self::$userToAttendeeId = [];

View file

@ -120,6 +120,20 @@ trait RecordingTrait {
$this->sendBackendRequestFromRecordingServer($data, $statusCode, $apiVersion);
}
/**
* @When /^recording server sent failed request for recording in room "([^"]*)" with (\d+)(?: \((v1)\))?$/
*/
public function recordingServerSentFailedRequestForRecordingInRoomWith(string $identifier, int $statusCode, string $apiVersion = 'v1') {
$data = [
'type' => 'failed',
'failed' => [
'token' => FeatureContext::getTokenForIdentifier($identifier),
],
];
$this->sendBackendRequestFromRecordingServer($data, $statusCode, $apiVersion);
}
private function sendBackendRequestFromRecordingServer(array $data, int $statusCode, string $apiVersion = 'v1') {
$body = json_encode($data);

View file

@ -89,6 +89,178 @@ Feature: callapi/recording
| type | name | callRecording |
| 2 | room1 | 0 |
Scenario: Recording failed to start
Given recording server is started
And the following "spreed" app config is set
| signaling_dev | yes |
And user "participant1" creates room "room1" (v4)
| roomType | 2 |
| roomName | room1 |
And user "participant1" joins room "room1" with 200 (v4)
And user "participant1" joins call "room1" with 200 (v4)
And user "participant1" starts "video" recording in room "room1" with 200 (v1)
And recording server received the following requests
| token | data |
| room1 | {"type":"start","start":{"status":1,"owner":"participant1","actor":{"type":"users","id":"participant1"}}} |
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 3 |
When recording server sent failed request for recording in room "room1" with 200
Then user "participant1" sees the following system messages in room "room1" with 200 (v1)
| room | actorType | actorId | actorDisplayName | systemMessage |
| room1 | users | participant1 | participant1-displayname | call_started |
| room1 | users | participant1 | participant1-displayname | conversation_created |
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 5 |
Scenario: Video recording failed
Given recording server is started
And the following "spreed" app config is set
| signaling_dev | yes |
And user "participant1" creates room "room1" (v4)
| roomType | 2 |
| roomName | room1 |
And user "participant1" joins room "room1" with 200 (v4)
And user "participant1" joins call "room1" with 200 (v4)
And user "participant1" starts "video" recording in room "room1" with 200 (v1)
And recording server received the following requests
| token | data |
| room1 | {"type":"start","start":{"status":1,"owner":"participant1","actor":{"type":"users","id":"participant1"}}} |
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 3 |
And recording server sent started request for "video" recording in room "room1" as "participant1" with 200
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 1 |
When recording server sent failed request for recording in room "room1" with 200
Then user "participant1" sees the following system messages in room "room1" with 200 (v1)
| room | actorType | actorId | actorDisplayName | systemMessage |
| room1 | guests | failed-to-get-session | | recording_failed |
| room1 | users | participant1 | participant1-displayname | recording_started |
| room1 | users | participant1 | participant1-displayname | call_started |
| room1 | users | participant1 | participant1-displayname | conversation_created |
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 5 |
Scenario: Start and stop recording again after the previous one failed to start
Given recording server is started
And the following "spreed" app config is set
| signaling_dev | yes |
And user "participant1" creates room "room1" (v4)
| roomType | 2 |
| roomName | room1 |
And user "participant1" joins room "room1" with 200 (v4)
And user "participant1" joins call "room1" with 200 (v4)
And user "participant1" starts "video" recording in room "room1" with 200 (v1)
And recording server received the following requests
| token | data |
| room1 | {"type":"start","start":{"status":1,"owner":"participant1","actor":{"type":"users","id":"participant1"}}} |
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 3 |
And recording server sent failed request for recording in room "room1" with 200
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 5 |
When user "participant1" starts "video" recording in room "room1" with 200 (v1)
And recording server received the following requests
| token | data |
| room1 | {"type":"start","start":{"status":1,"owner":"participant1","actor":{"type":"users","id":"participant1"}}} |
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 3 |
And recording server sent started request for "video" recording in room "room1" as "participant1" with 200
Then user "participant1" sees the following system messages in room "room1" with 200 (v1)
| room | actorType | actorId | actorDisplayName | systemMessage |
| room1 | users | participant1 | participant1-displayname | recording_started |
| room1 | users | participant1 | participant1-displayname | call_started |
| room1 | users | participant1 | participant1-displayname | conversation_created |
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 1 |
When user "participant1" stops recording in room "room1" with 200 (v1)
And recording server received the following requests
| token | data |
| room1 | {"type":"stop","stop":{"actor":{"type":"users","id":"participant1"}}} |
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 1 |
And recording server sent stopped request for recording in room "room1" as "participant1" with 200
Then user "participant1" sees the following system messages in room "room1" with 200 (v1)
| room | actorType | actorId | actorDisplayName | systemMessage |
| room1 | users | participant1 | participant1-displayname | recording_stopped |
| room1 | users | participant1 | participant1-displayname | recording_started |
| room1 | users | participant1 | participant1-displayname | call_started |
| room1 | users | participant1 | participant1-displayname | conversation_created |
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 0 |
Scenario: Start and stop recording again after the previous one failed
Given recording server is started
And the following "spreed" app config is set
| signaling_dev | yes |
And user "participant1" creates room "room1" (v4)
| roomType | 2 |
| roomName | room1 |
And user "participant1" joins room "room1" with 200 (v4)
And user "participant1" joins call "room1" with 200 (v4)
And user "participant1" starts "video" recording in room "room1" with 200 (v1)
And recording server received the following requests
| token | data |
| room1 | {"type":"start","start":{"status":1,"owner":"participant1","actor":{"type":"users","id":"participant1"}}} |
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 3 |
And recording server sent started request for "video" recording in room "room1" as "participant1" with 200
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 1 |
And recording server sent failed request for recording in room "room1" with 200
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 5 |
When user "participant1" starts "video" recording in room "room1" with 200 (v1)
And recording server received the following requests
| token | data |
| room1 | {"type":"start","start":{"status":1,"owner":"participant1","actor":{"type":"users","id":"participant1"}}} |
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 3 |
And recording server sent started request for "video" recording in room "room1" as "participant1" with 200
Then user "participant1" sees the following system messages in room "room1" with 200 (v1)
| room | actorType | actorId | actorDisplayName | systemMessage |
| room1 | users | participant1 | participant1-displayname | recording_started |
| room1 | guests | failed-to-get-session | | recording_failed |
| room1 | users | participant1 | participant1-displayname | recording_started |
| room1 | users | participant1 | participant1-displayname | call_started |
| room1 | users | participant1 | participant1-displayname | conversation_created |
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 1 |
When user "participant1" stops recording in room "room1" with 200 (v1)
And recording server received the following requests
| token | data |
| room1 | {"type":"stop","stop":{"actor":{"type":"users","id":"participant1"}}} |
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 1 |
And recording server sent stopped request for recording in room "room1" as "participant1" with 200
Then user "participant1" sees the following system messages in room "room1" with 200 (v1)
| room | actorType | actorId | actorDisplayName | systemMessage |
| room1 | users | participant1 | participant1-displayname | recording_stopped |
| room1 | users | participant1 | participant1-displayname | recording_started |
| room1 | guests | failed-to-get-session | | recording_failed |
| room1 | users | participant1 | participant1-displayname | recording_started |
| room1 | users | participant1 | participant1-displayname | call_started |
| room1 | users | participant1 | participant1-displayname | conversation_created |
And user "participant1" is participant of the following unordered rooms (v4)
| type | name | callRecording |
| 2 | room1 | 0 |
Scenario: Get error when start|stop recording and already did this
Given recording server is started
And the following "spreed" app config is set

View file

@ -461,6 +461,90 @@ class ListenerTest extends TestCase {
'bob',
['message' => 'audio_recording_stopped', 'parameters' => []],
],
[
Room::RECORDING_FAILED,
Room::RECORDING_VIDEO_STARTING,
null,
null,
null,
],
[
Room::RECORDING_FAILED,
Room::RECORDING_AUDIO_STARTING,
null,
null,
null,
],
[
Room::RECORDING_FAILED,
Room::RECORDING_VIDEO,
null,
null,
['message' => 'recording_failed', 'parameters' => []],
],
[
Room::RECORDING_FAILED,
Room::RECORDING_AUDIO,
null,
null,
['message' => 'recording_failed', 'parameters' => []],
],
[
Room::RECORDING_VIDEO_STARTING,
Room::RECORDING_FAILED,
null,
null,
null,
],
[
Room::RECORDING_VIDEO_STARTING,
Room::RECORDING_FAILED,
Attendee::ACTOR_USERS,
'alice',
null,
],
[
Room::RECORDING_VIDEO,
Room::RECORDING_FAILED,
null,
null,
['message' => 'recording_started', 'parameters' => []],
],
[
Room::RECORDING_VIDEO,
Room::RECORDING_FAILED,
Attendee::ACTOR_USERS,
'alice',
['message' => 'recording_started', 'parameters' => []],
],
[
Room::RECORDING_AUDIO_STARTING,
Room::RECORDING_FAILED,
null,
null,
null,
],
[
Room::RECORDING_AUDIO_STARTING,
Room::RECORDING_FAILED,
Attendee::ACTOR_USERS,
'alice',
null,
],
[
Room::RECORDING_AUDIO,
Room::RECORDING_FAILED,
null,
null,
['message' => 'audio_recording_started', 'parameters' => []],
],
[
Room::RECORDING_AUDIO,
Room::RECORDING_FAILED,
Attendee::ACTOR_USERS,
'alice',
['message' => 'audio_recording_started', 'parameters' => []],
],
];
}