Skip to content

Commit

Permalink
Added endpoint to save recorded videos (#15)
Browse files Browse the repository at this point in the history
* Added endpoint to save recorded videos

* Fix tests

* Fix tests
  • Loading branch information
daVitekPL authored Oct 11, 2024
1 parent b18301e commit 6037407
Show file tree
Hide file tree
Showing 23 changed files with 524 additions and 108 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test-cc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:

services:
mysql:
image: mariadb
image: mysql:5.7
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: database
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:

services:
mysql:
image: mariadb
image: mysql:5.7
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: database
Expand Down Expand Up @@ -61,7 +61,7 @@ jobs:

services:
mysql:
image: mariadb
image: mysql:5.7
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: database
Expand Down
3 changes: 2 additions & 1 deletion config/jaas.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
'iss' => env('JAAS_ISS', 'chat'),
'sub' => env('JAAS_SUB', ''),
'kid' => env('JAAS_KEY_ID', ''),
'private_key' => env('JAAS_PRIVATE_KEY', '')
'private_key' => env('JAAS_PRIVATE_KEY', ''),
'recording' => env('JAAS_RECORDING', false),
];
4 changes: 2 additions & 2 deletions config/jitsi.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
'iss' => env('JAAS_ISS', 'chat'),
'sub' => env('JAAS_SUB', ''),
'kid' => env('JAAS_KEY_ID', ''),
'private_key' => env('JAAS_PRIVATE_KEY', '')

'private_key' => env('JAAS_PRIVATE_KEY', ''),
'recording' => env('JAAS_RECORDING', false),
];
37 changes: 37 additions & 0 deletions src/Dto/RecordedVideoDataDto.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace EscolaLms\Jitsi\Dto;

class RecordedVideoDataDto
{
private array $participants;
private string $initiatorId;
private $durationSec;
private string $startTimestamp;
private string $endTimestamp;
private string $recordingSessionId;
private string $preAuthenticatedLink;
private bool|null $share;

public function __construct(array $participants, string $initiatorId, int $durationSec, string $startTimestamp, string $endTimestamp, string $recordingSessionId, string $preAuthenticatedLink, bool|null $share)
{
$this->participants = $participants;
$this->initiatorId = $initiatorId;
$this->durationSec = $durationSec;
$this->startTimestamp = $startTimestamp;
$this->endTimestamp = $endTimestamp;
$this->recordingSessionId = $recordingSessionId;
$this->preAuthenticatedLink = $preAuthenticatedLink;
$this->share = $share;
}

public function getPreAuthenticatedLink(): string
{
return $this->preAuthenticatedLink;
}

public function getStartTimestamp(): string
{
return $this->startTimestamp;
}
}
53 changes: 53 additions & 0 deletions src/Dto/RecordedVideoDto.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

namespace EscolaLms\Jitsi\Dto;

use EscolaLms\Core\Dtos\Contracts\DtoContract;
use EscolaLms\Core\Dtos\Contracts\InstantiateFromRequest;
use Illuminate\Http\Request;

class RecordedVideoDto implements DtoContract, InstantiateFromRequest
{
private string $eventType;
private string $timestamp;
private string $sessionId;
private string $fqn;
private string $appId;
private RecordedVideoDataDto $data;

public function __construct(string $eventType, string $timestamp, string $sessionId, string $fqn, string $appId, array $data)
{
$this->eventType = $eventType;
$this->timestamp = $timestamp;
$this->sessionId = $sessionId;
$this->fqn = $fqn;
$this->appId = $appId;
$this->data = new RecordedVideoDataDto(...$data);
}

public function toArray(): array
{
return [];
}

public static function instantiateFromRequest(Request $request): self
{
return new static(
$request->input('eventType'),
$request->input('timestamp'),
$request->input('sessionId'),
$request->input('fqn'),
$request->input('appId'),
$request->input('data'),
);
}
public function getFqn(): string
{
return $this->fqn;
}

public function getData(): RecordedVideoDataDto
{
return $this->data;
}
}
10 changes: 10 additions & 0 deletions src/Enum/JitsiEventsEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace EscolaLms\Jitsi\Enum;

use EscolaLms\Core\Enums\BasicEnum;

class JitsiEventsEnum extends BasicEnum
{
const RECORDING_UPLOADED = 'RECORDING_UPLOADED';
}
19 changes: 19 additions & 0 deletions src/EscolaLmsJitsiServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

use EscolaLms\Jitsi\Providers\SettingsServiceProvider;
use EscolaLms\Jitsi\Services\Contracts\JaasServiceContract;
use EscolaLms\Jitsi\Services\Contracts\JitsiVideoServiceContract;
use EscolaLms\Jitsi\Services\JaasService;
use EscolaLms\Jitsi\Services\JitsiVideoService;
use Illuminate\Support\ServiceProvider;
use EscolaLms\Jitsi\Services\Contracts\JitsiServiceContract;
use EscolaLms\Jitsi\Services\JitsiService;
Expand All @@ -18,6 +20,7 @@ class EscolaLmsJitsiServiceProvider extends ServiceProvider
public $singletons = [
JitsiServiceContract::class => JitsiService::class,
JaasServiceContract::class => JaasService::class,
JitsiVideoServiceContract::class => JitsiVideoService::class,
];

/**
Expand All @@ -27,6 +30,22 @@ class EscolaLmsJitsiServiceProvider extends ServiceProvider
*/
public function boot()
{
$this->loadRoutesFrom(__DIR__ . '/routes.php');

if ($this->app->runningInConsole()) {
$this->bootForConsole();
}
}

protected function bootForConsole(): void
{
$this->publishes([
__DIR__ . '/../config/jitsi.php' => config_path('jitsi.php'),
], 'jitsi.config');

$this->publishes([
__DIR__ . '/../config/jaas.php' => config_path('jaas.php'),
], 'jaas.config');
}

public function register()
Expand Down
26 changes: 26 additions & 0 deletions src/Exceptions/InvalidJitsiFqnException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace EscolaLms\Jitsi\Exceptions;

use Exception;
use Illuminate\Http\JsonResponse;

class InvalidJitsiFqnException extends Exception
{
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: __('Invalid fqn');
$code = $code ?: 422;
parent::__construct($message, $code, $previous);
}

public function render($request): JsonResponse
{
return response()->json([
'data' => [
'code' => $this->getCode(),
'message' => $this->getMessage()
]
], $this->getCode());
}
}
26 changes: 26 additions & 0 deletions src/Exceptions/RecordedVideoSaveException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace EscolaLms\Jitsi\Exceptions;

use Exception;
use Illuminate\Http\JsonResponse;

class RecordedVideoSaveException extends Exception
{
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: __('Error while saving jitsi recorded video');
$code = $code ?: 400;
parent::__construct($message, $code, $previous);
}

public function render($request): JsonResponse
{
return response()->json([
'data' => [
'code' => $this->getCode(),
'message' => $this->getMessage()
]
], $this->getCode());
}
}
6 changes: 4 additions & 2 deletions src/Helpers/StringHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

class StringHelper
{
public static function convertToJitsiSlug(string $str, array $options = []): string
public static function convertToJitsiSlug(string $str, array $options = [], string|null $type = null, int|null $modelId = null, string|null $subModel = null): string
{
// Make sure string is in UTF-8 and strip invalid UTF-8 characters
$str = mb_convert_encoding($str, 'UTF-8', mb_list_encodings());
Expand Down Expand Up @@ -43,7 +43,9 @@ public static function convertToJitsiSlug(string $str, array $options = []): str

// Remove delimiter from ends
$str = trim($str, $options['delimiter']);
return $options['lowercase'] ? mb_strtolower($str, 'UTF-8') : $str;
$str = $options['lowercase'] ? mb_strtolower($str, 'UTF-8') : $str;
$suffix = ($type ? "_{$type}" : '') . ($modelId ? "_{$modelId}" : '') . ($subModel ? "_{$subModel}" : '');
return "{$str}{$suffix}";
}

}
22 changes: 22 additions & 0 deletions src/Http/Controllers/JitsiApiController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace EscolaLms\Jitsi\Http\Controllers;

use EscolaLms\Core\Http\Controllers\EscolaLmsBaseController;
use EscolaLms\Jitsi\Dto\RecordedVideoDto;
use EscolaLms\Jitsi\Http\Requests\RecordedVideoRequest;
use EscolaLms\Jitsi\Services\Contracts\JitsiVideoServiceContract;
use Illuminate\Http\JsonResponse;

class JitsiApiController extends EscolaLmsBaseController
{
public function __construct(
private JitsiVideoServiceContract $jitsiVideoService,
) {}

public function recordedVideo(RecordedVideoRequest $request): JsonResponse
{
$this->jitsiVideoService->recordedVideo(RecordedVideoDto::instantiateFromRequest($request));
return $this->sendSuccess(__('Screen saved successfully'));
}
}
30 changes: 30 additions & 0 deletions src/Http/Requests/RecordedVideoRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace EscolaLms\Jitsi\Http\Requests;

use EscolaLms\Jitsi\Enum\JitsiEventsEnum;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class RecordedVideoRequest extends FormRequest
{
public function rules(): array
{
return [
'eventType' => ['required', 'string', Rule::in(JitsiEventsEnum::RECORDING_UPLOADED)],
'timestamp' => ['required', 'numeric'],
'sessionId' => ['required', 'string'],
'fqn' => ['required', 'string'],
'appId' => ['required', 'string', Rule::in([config('jitsi.app_id')])],
'data' => ['required', 'array'],
'data.participants' => ['required', 'array'],
'data.share' => ['nullable', 'boolean'],
'data.initiatorId' => ['required', 'string'],
'data.durationSec' => ['required', 'numeric'],
'data.startTimestamp' => ['required', 'numeric'],
'data.endTimestamp' => ['required', 'numeric'],
'data.recordingSessionId' => ['required', 'string'],
'data.preAuthenticatedLink' => ['required', 'string'],
];
}
}
2 changes: 2 additions & 0 deletions src/Providers/SettingsServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public function register()
AdministrableConfig::registerConfig(self::CONFIG_KEY . '.iss', ['nullable', 'string'], false);
AdministrableConfig::registerConfig(self::CONFIG_KEY . '.kid', ['nullable', 'string'], false);
AdministrableConfig::registerConfig(self::CONFIG_KEY . '.private_key', ['nullable', 'string'], false);
AdministrableConfig::registerConfig(self::CONFIG_KEY . '.sub', ['nullable', 'string', false]);
AdministrableConfig::registerConfig(self::CONFIG_KEY . '.recording', ['nullable', 'boolean', false]);
}
}
}
10 changes: 10 additions & 0 deletions src/Services/Contracts/JitsiVideoServiceContract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace EscolaLms\Jitsi\Services\Contracts;

use EscolaLms\Jitsi\Dto\RecordedVideoDto;

interface JitsiVideoServiceContract
{
public function recordedVideo(RecordedVideoDto $dto): void;
}
11 changes: 11 additions & 0 deletions src/Services/FileService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace EscolaLms\Jitsi\Services;

class FileService
{
public function getFileFromUrl(string $url): bool|string
{
return file_get_contents($url);
}
}
11 changes: 11 additions & 0 deletions src/Services/JaasService.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,24 @@ public function generateJwt(
int $expireInMinutes = 60
): string {
$userData = $this->getUserData($user, $isModerator);
$features = [];
if ($isModerator) {
$features = [
'livestreaming' => true,
'outbound-call' => true,
'sip-outbound-call' => false,
'transcriptions' => true,
'recording' => $this->config['recording'],
];
}
$payload = [
'aud' => $this->config['aud'],
'iss' => $this->config['iss'],
'exp' => now()->addMinutes($expireInMinutes)->timestamp,
'sub' => $this->config['app_id'],
'room' => $room,
'context' => [
'features' => $features,
'user' => $userData,
],
];
Expand Down
4 changes: 2 additions & 2 deletions src/Services/JitsiService.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,15 @@ public function getChannelData(
'VideoConferenceModeStrategy',
'generateJwt',
$user,
$channelName,
$channelDisplayName,
$isModerator
);
$url = StrategyHelper::useStrategyPattern(
$className,
'VideoConferenceModeStrategy',
'getUrl',
$jwt,
$channelName
$channelDisplayName
);
}
if (!empty($jwt)) {
Expand Down
Loading

0 comments on commit 6037407

Please sign in to comment.