Add Contabo Object Storage support for Meet egress recordings.
Deploy Ladill Meet / deploy (push) Successful in 49s
Deploy Ladill Meet / deploy (push) Successful in 49s
Use a dedicated meet-recordings S3 disk with path-style endpoints for Contabo, and pass force_path_style through to LiveKit egress uploads. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+7
-3
@@ -45,16 +45,20 @@ LIVEKIT_API_KEY=
|
|||||||
LIVEKIT_API_SECRET=
|
LIVEKIT_API_SECRET=
|
||||||
|
|
||||||
# --- Recordings & AI ---
|
# --- Recordings & AI ---
|
||||||
MEET_RECORDINGS_DISK=local
|
# Use meet-recordings disk when LiveKit egress writes to Contabo Object Storage.
|
||||||
|
MEET_RECORDINGS_DISK=meet-recordings
|
||||||
MEET_RECORDING_MAX_MB=512
|
MEET_RECORDING_MAX_MB=512
|
||||||
# Server-side recording via LiveKit Egress (recommended for production)
|
# Server-side recording via LiveKit Egress (recommended for production)
|
||||||
MEET_EGRESS_ENABLED=false
|
MEET_EGRESS_ENABLED=false
|
||||||
MEET_EGRESS_WEBHOOK_URL=
|
MEET_EGRESS_WEBHOOK_URL=
|
||||||
|
# Contabo Object Storage (S3-compatible). Keys: Object Storage panel → Account → Security & Access.
|
||||||
|
# Endpoint examples: https://eu2.contabostorage.com https://sin1.contabostorage.com
|
||||||
MEET_EGRESS_S3_BUCKET=
|
MEET_EGRESS_S3_BUCKET=
|
||||||
MEET_EGRESS_S3_REGION=
|
MEET_EGRESS_S3_REGION=default
|
||||||
|
MEET_EGRESS_S3_ENDPOINT=https://eu2.contabostorage.com
|
||||||
|
MEET_EGRESS_S3_USE_PATH_STYLE=true
|
||||||
MEET_EGRESS_S3_ACCESS_KEY=
|
MEET_EGRESS_S3_ACCESS_KEY=
|
||||||
MEET_EGRESS_S3_SECRET=
|
MEET_EGRESS_S3_SECRET=
|
||||||
MEET_EGRESS_S3_ENDPOINT=
|
|
||||||
MEET_AI_DRIVER=openai
|
MEET_AI_DRIVER=openai
|
||||||
MEET_AI_API_KEY=
|
MEET_AI_API_KEY=
|
||||||
MEET_AI_MODEL=gpt-4o-mini
|
MEET_AI_MODEL=gpt-4o-mini
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class LiveKitEgressService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string, string>|null
|
* @return array<string, bool|string>|null
|
||||||
*/
|
*/
|
||||||
protected function s3UploadConfig(): ?array
|
protected function s3UploadConfig(): ?array
|
||||||
{
|
{
|
||||||
@@ -103,7 +103,8 @@ class LiveKitEgressService
|
|||||||
|
|
||||||
$config = [
|
$config = [
|
||||||
'bucket' => $bucket,
|
'bucket' => $bucket,
|
||||||
'region' => (string) ($egress['region'] ?? 'us-east-1'),
|
'region' => (string) ($egress['region'] ?? 'default'),
|
||||||
|
'force_path_style' => (bool) ($egress['force_path_style'] ?? true),
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach (['access_key', 'secret', 'session_token', 'endpoint', 'assume_role_arn', 'assume_role_external_id'] as $key) {
|
foreach (['access_key', 'secret', 'session_token', 'endpoint', 'assume_role_arn', 'assume_role_external_id'] as $key) {
|
||||||
|
|||||||
@@ -60,6 +60,23 @@ return [
|
|||||||
'report' => false,
|
'report' => false,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// Contabo (or other S3-compatible) bucket for Meet recordings — same creds as LiveKit egress.
|
||||||
|
'meet-recordings' => [
|
||||||
|
'driver' => 's3',
|
||||||
|
'key' => env('MEET_EGRESS_S3_ACCESS_KEY', env('AWS_ACCESS_KEY_ID')),
|
||||||
|
'secret' => env('MEET_EGRESS_S3_SECRET', env('AWS_SECRET_ACCESS_KEY')),
|
||||||
|
'region' => env('MEET_EGRESS_S3_REGION', env('AWS_DEFAULT_REGION', 'default')),
|
||||||
|
'bucket' => env('MEET_EGRESS_S3_BUCKET', env('AWS_BUCKET')),
|
||||||
|
'url' => env('MEET_EGRESS_S3_URL', env('AWS_URL')),
|
||||||
|
'endpoint' => env('MEET_EGRESS_S3_ENDPOINT', env('AWS_ENDPOINT')),
|
||||||
|
'use_path_style_endpoint' => filter_var(
|
||||||
|
env('MEET_EGRESS_S3_USE_PATH_STYLE', env('AWS_USE_PATH_STYLE_ENDPOINT', true)),
|
||||||
|
FILTER_VALIDATE_BOOL,
|
||||||
|
),
|
||||||
|
'throw' => false,
|
||||||
|
'report' => false,
|
||||||
|
],
|
||||||
|
|
||||||
'qr' => [
|
'qr' => [
|
||||||
'driver' => 'local',
|
'driver' => 'local',
|
||||||
'root' => storage_path('app/private/qr'),
|
'root' => storage_path('app/private/qr'),
|
||||||
|
|||||||
+5
-1
@@ -121,9 +121,13 @@ return [
|
|||||||
'access_key' => env('MEET_EGRESS_S3_ACCESS_KEY', env('AWS_ACCESS_KEY_ID')),
|
'access_key' => env('MEET_EGRESS_S3_ACCESS_KEY', env('AWS_ACCESS_KEY_ID')),
|
||||||
'secret' => env('MEET_EGRESS_S3_SECRET', env('AWS_SECRET_ACCESS_KEY')),
|
'secret' => env('MEET_EGRESS_S3_SECRET', env('AWS_SECRET_ACCESS_KEY')),
|
||||||
'session_token' => env('MEET_EGRESS_S3_SESSION_TOKEN'),
|
'session_token' => env('MEET_EGRESS_S3_SESSION_TOKEN'),
|
||||||
'region' => env('MEET_EGRESS_S3_REGION', env('AWS_DEFAULT_REGION', 'us-east-1')),
|
'region' => env('MEET_EGRESS_S3_REGION', env('AWS_DEFAULT_REGION', 'default')),
|
||||||
'bucket' => env('MEET_EGRESS_S3_BUCKET', env('AWS_BUCKET')),
|
'bucket' => env('MEET_EGRESS_S3_BUCKET', env('AWS_BUCKET')),
|
||||||
'endpoint' => env('MEET_EGRESS_S3_ENDPOINT', env('AWS_ENDPOINT')),
|
'endpoint' => env('MEET_EGRESS_S3_ENDPOINT', env('AWS_ENDPOINT')),
|
||||||
|
'force_path_style' => filter_var(
|
||||||
|
env('MEET_EGRESS_S3_USE_PATH_STYLE', env('AWS_USE_PATH_STYLE_ENDPOINT', true)),
|
||||||
|
FILTER_VALIDATE_BOOL,
|
||||||
|
),
|
||||||
'assume_role_arn' => env('MEET_EGRESS_S3_ASSUME_ROLE_ARN'),
|
'assume_role_arn' => env('MEET_EGRESS_S3_ASSUME_ROLE_ARN'),
|
||||||
'assume_role_external_id' => env('MEET_EGRESS_S3_ASSUME_ROLE_EXTERNAL_ID'),
|
'assume_role_external_id' => env('MEET_EGRESS_S3_ASSUME_ROLE_EXTERNAL_ID'),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -203,6 +203,28 @@ class MeetLiveKitEgressRecordingTest extends TestCase
|
|||||||
$this->assertSame('encoder crashed', $recording->failure_reason);
|
$this->assertSame('encoder crashed', $recording->failure_reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_s3_upload_config_uses_contabo_path_style_defaults(): void
|
||||||
|
{
|
||||||
|
config([
|
||||||
|
'meet.recordings.egress.s3.bucket' => 'ladill-meet',
|
||||||
|
'meet.recordings.egress.s3.endpoint' => 'https://eu2.contabostorage.com',
|
||||||
|
'meet.recordings.egress.s3.region' => 'default',
|
||||||
|
'meet.recordings.egress.s3.force_path_style' => true,
|
||||||
|
'meet.recordings.egress.s3.access_key' => 'access',
|
||||||
|
'meet.recordings.egress.s3.secret' => 'secret',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$service = app(LiveKitEgressService::class);
|
||||||
|
$method = new \ReflectionMethod($service, 's3UploadConfig');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
$config = $method->invoke($service);
|
||||||
|
|
||||||
|
$this->assertSame('ladill-meet', $config['bucket']);
|
||||||
|
$this->assertSame('https://eu2.contabostorage.com', $config['endpoint']);
|
||||||
|
$this->assertSame('default', $config['region']);
|
||||||
|
$this->assertTrue($config['force_path_style']);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array{0: Session, 1: Participant}
|
* @return array{0: Session, 1: Participant}
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user