summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoas Schilling <213943+nickvergessen@users.noreply.github.com>2023-02-22 13:37:54 +0100
committerGitHub <noreply@github.com>2023-02-22 13:37:54 +0100
commit822e1898ee83544c61a72fc59b06809d533c930e (patch)
treec106ccb9dab8ff2b763cd28b93464452876fb534
parent69b43cc0134a4487d58b1eb2f76485a1f5161236 (diff)
parent84747d40df631aeba406cf3b1c535cf6803a94e0 (diff)
Merge pull request #8720 from nextcloud/change-recording-status-when-notified-by-recording-server
Change recording status when notified by recording server
-rw-r--r--appinfo/routes/routesRecordingController.php2
-rw-r--r--docs/constants.md3
-rw-r--r--docs/recording.md85
-rw-r--r--lib/Activity/Listener.php4
-rw-r--r--lib/Chat/Parser/SystemMessage.php2
-rw-r--r--lib/Chat/SystemMessage/Listener.php24
-rw-r--r--lib/Controller/RecordingController.php158
-rw-r--r--lib/Exceptions/RecordingNotFoundException.php27
-rw-r--r--lib/Recording/BackendNotifier.php35
-rw-r--r--lib/Room.php3
-rw-r--r--lib/Service/RecordingService.php26
-rw-r--r--lib/Service/RoomService.php8
-rw-r--r--psalm.xml1
-rw-r--r--recording/docs/recording-api.md22
-rw-r--r--recording/src/nextcloud/talk/recording/BackendNotifier.py97
-rw-r--r--recording/src/nextcloud/talk/recording/Server.py65
-rw-r--r--recording/src/nextcloud/talk/recording/Service.py21
-rw-r--r--src/components/DeviceChecker/DeviceChecker.vue10
-rw-r--r--src/components/TopBar/CallButton.vue10
-rw-r--r--src/components/TopBar/CallTime.vue33
-rw-r--r--src/components/TopBar/TopBarMenu.vue20
-rw-r--r--src/constants.js3
-rw-r--r--src/init.js16
-rw-r--r--src/store/conversationsStore.js15
-rw-r--r--src/utils/webrtc/index.js7
-rw-r--r--tests/integration/features/bootstrap/FakeRecordingServer.php21
-rw-r--r--tests/integration/features/bootstrap/FeatureContext.php3
-rw-r--r--tests/integration/features/bootstrap/RecordingTrait.php95
-rw-r--r--tests/integration/features/callapi/recording.feature250
-rw-r--r--tests/php/Chat/SystemMessage/ListenerTest.php288
-rw-r--r--tests/php/Recording/BackendNotifierTest.php62
-rw-r--r--tests/stubs/GuzzleHttp_Exception_ClientException.php8
32 files changed, 1350 insertions, 74 deletions
diff --git a/appinfo/routes/routesRecordingController.php b/appinfo/routes/routesRecordingController.php
index 6d984ad3d..d2d0ad690 100644
--- a/appinfo/routes/routesRecordingController.php
+++ b/appinfo/routes/routesRecordingController.php
@@ -34,6 +34,8 @@ return [
['name' => 'Recording#getWelcomeMessage', 'url' => '/api/{apiVersion}/recording/welcome/{serverId}', 'verb' => 'GET', 'requirements' => array_merge($requirements, [
'serverId' => '\d+',
])],
+ /** @see \OCA\Talk\Controller\RecordingController::backend() */
+ ['name' => 'Recording#backend', 'url' => '/api/{apiVersion}/recording/backend', 'verb' => 'POST', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\RecordingController::start() */
['name' => 'Recording#start', 'url' => '/api/{apiVersion}/recording/{token}', 'verb' => 'POST', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\RecordingController::stop() */
diff --git a/docs/constants.md b/docs/constants.md
index e9d8933bc..f227a894c 100644
--- a/docs/constants.md
+++ b/docs/constants.md
@@ -111,6 +111,9 @@ title: Constants
* `0` - No recording
* `1` - Recording video
* `2` - Recording audio
+* `3` - Starting video recording
+* `4` - Starting audio recording
+* `5` - Recording failed
## Chat
diff --git a/docs/recording.md b/docs/recording.md
index 6797d1ea9..bef05eae0 100644
--- a/docs/recording.md
+++ b/docs/recording.md
@@ -110,3 +110,88 @@
+ `400 Bad Request` Error: `system`: Internal system error
+ `403 Forbidden` When the user is not a moderator/owne
+ `404 Not Found` Room not found
+
+## Recording server requests
+
+* Required capability: `recording-v1`
+* Method: `POST`
+* Endpoint: `/recording/backend`
+
+* Header:
+
+| field | type | Description |
+| ------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------- |
+| `TALK_RECORDING_RANDOM` | string | Random string that needs to be concatenated with request body to generate the checksum using the secret configured for the backend. |
+| `TALK_RECORDING_CHECKSUM` | string | The checksum generated with `TALK_RECORDING_RANDOM`. |
+
+* Data:
+
+ - Body as a JSON encoded string; format depends on the request type, see below.
+
+* Response:
+ - Status code:
+ + `200 OK`
+ + `400 Bad Request`: When the body data does not match the expected format.
+ + `403 Forbidden`: When the request validation failed.
+
+### Started call recording
+
+* Data format:
+
+ ```json
+ {
+ "type": "started",
+ "started": {
+ "token": "the-token-of-the-room",
+ "status": "the-type-of-recording (see [Constants - Call recording status](constants.md#call-recording-status))",
+ "actor": {
+ "type": "the-type-of-the-actor",
+ "id": "the-id-of-the-actor",
+ },
+ },
+ }
+ ```
+
+* Response:
+ - (Additional) Status code:
+ + `404 Not Found`: When the room is not found.
+
+### Stopped call recording
+
+* Data format:
+
+ ```json
+ {
+ "type": "stopped",
+ "stopped": {
+ "token": "the-token-of-the-room",
+ "actor": {
+ "type": "the-type-of-the-actor",
+ "id": "the-id-of-the-actor",
+ },
+ },
+ }
+ ```
+
+ - `actor` is optional
+
+* 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.
diff --git a/lib/Activity/Listener.php b/lib/Activity/Listener.php
index 52a54725e..e42b63278 100644
--- a/lib/Activity/Listener.php
+++ b/lib/Activity/Listener.php
@@ -150,8 +150,8 @@ class Listener {
return false;
}
- if ($room->getCallRecording() !== Room::RECORDING_NONE) {
- $this->recordingService->stop($room);
+ if ($room->getCallRecording() !== Room::RECORDING_NONE && $room->getCallRecording() !== Room::RECORDING_FAILED) {
+ $this->recordingService->stop($room, $actor);
}
if ($actor instanceof Participant) {
$actorId = $actor->getAttendee()->getActorId();
diff --git a/lib/Chat/Parser/SystemMessage.php b/lib/Chat/Parser/SystemMessage.php
index 673d7121f..503d722ee 100644
--- a/lib/Chat/Parser/SystemMessage.php
+++ b/lib/Chat/Parser/SystemMessage.php
@@ -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'];
diff --git a/lib/Chat/SystemMessage/Listener.php b/lib/Chat/SystemMessage/Listener.php
index 9e33a8aca..e54972e22 100644
--- a/lib/Chat/SystemMessage/Listener.php
+++ b/lib/Chat/SystemMessage/Listener.php
@@ -501,12 +501,23 @@ 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, 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 && !$recordingHasFailed) {
+ return;
+ }
+
$prefix = self::getCallRecordingPrefix($event);
$suffix = self::getCallRecordingSuffix($event);
$systemMessage = $prefix . 'recording_' . $suffix;
$listener = Server::get(self::class);
- $listener->sendSystemMessage($event->getRoom(), $systemMessage);
+ $listener->sendSystemMessage($event->getRoom(), $systemMessage, [], $event->getActor());
}
private static function getCallRecordingSuffix(ModifyRoomEvent $event): string {
@@ -515,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_' : '';
}
diff --git a/lib/Controller/RecordingController.php b/lib/Controller/RecordingController.php
index a79cf2148..1f55bee1d 100644
--- a/lib/Controller/RecordingController.php
+++ b/lib/Controller/RecordingController.php
@@ -28,7 +28,13 @@ namespace OCA\Talk\Controller;
use InvalidArgumentException;
use GuzzleHttp\Exception\ConnectException;
use OCA\Talk\Config;
+use OCA\Talk\Exceptions\ParticipantNotFoundException;
+use OCA\Talk\Exceptions\RoomNotFoundException;
+use OCA\Talk\Manager;
+use OCA\Talk\Room;
+use OCA\Talk\Service\ParticipantService;
use OCA\Talk\Service\RecordingService;
+use OCA\Talk\Service\RoomService;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\Http\Client\IClientService;
@@ -42,7 +48,10 @@ class RecordingController extends AEnvironmentAwareController {
private ?string $userId,
private Config $talkConfig,
private IClientService $clientService,
+ private Manager $manager,
+ private ParticipantService $participantService,
private RecordingService $recordingService,
+ private RoomService $roomService,
private LoggerInterface $logger
) {
parent::__construct($appName, $request);
@@ -110,12 +119,157 @@ class RecordingController extends AEnvironmentAwareController {
}
/**
+ * Return the body of the backend request. This can be overridden in
+ * tests.
+ *
+ * @return string
+ */
+ protected function getInputStream(): string {
+ return file_get_contents('php://input');
+ }
+
+ /**
+ * Backend API to update recording status by backends.
+ *
+ * @PublicPage
+ * @BruteForceProtection(action=talkRecordingSecret)
+ *
+ * @return DataResponse
+ */
+ public function backend(): DataResponse {
+ $json = $this->getInputStream();
+ if (!$this->validateBackendRequest($json)) {
+ $response = new DataResponse([
+ 'type' => 'error',
+ 'error' => [
+ 'code' => 'invalid_request',
+ 'message' => 'The request could not be authenticated.',
+ ],
+ ], Http::STATUS_FORBIDDEN);
+ $response->throttle();
+ return $response;
+ }
+
+ $message = json_decode($json, true);
+ switch ($message['type'] ?? '') {
+ case 'started':
+ 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',
+ 'error' => [
+ 'code' => 'unknown_type',
+ 'message' => 'The given type ' . json_encode($message) . ' is not supported.',
+ ],
+ ], Http::STATUS_BAD_REQUEST);
+ }
+ }
+
+ private fu