Deploy Ladill Meet / deploy (push) Successful in 52s
Server-side room composite egress replaces fragile MediaRecorder DOM capture when enabled, with webhook completion and browser fallback when egress is unavailable. Co-authored-by: Cursor <cursoragent@cursor.com>
58 lines
1.7 KiB
PHP
58 lines
1.7 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use Agence104\LiveKit\WebhookReceiver;
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Recording;
|
|
use App\Services\Meet\RecordingService;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class LiveKitWebhookController extends Controller
|
|
{
|
|
public function __invoke(Request $request, RecordingService $recordings): JsonResponse
|
|
{
|
|
try {
|
|
$receiver = new WebhookReceiver(
|
|
(string) config('meet.media.livekit.api_key'),
|
|
(string) config('meet.media.livekit.api_secret'),
|
|
);
|
|
|
|
$event = $receiver->receive(
|
|
$request->getContent(),
|
|
$request->header('Authorization'),
|
|
);
|
|
} catch (\Throwable $e) {
|
|
Log::warning('LiveKit webhook rejected.', ['error' => $e->getMessage()]);
|
|
|
|
return response()->json(['error' => 'invalid webhook'], 401);
|
|
}
|
|
|
|
if ($event->getEvent() !== 'egress_ended' || ! $event->hasEgressInfo()) {
|
|
return response()->json(['status' => 'ignored']);
|
|
}
|
|
|
|
$info = $event->getEgressInfo();
|
|
$egressId = $info->getEgressId();
|
|
|
|
if ($egressId === '') {
|
|
return response()->json(['status' => 'ignored']);
|
|
}
|
|
|
|
$recording = Recording::query()
|
|
->whereIn('status', ['recording', 'processing'])
|
|
->where('metadata->egress_id', $egressId)
|
|
->first();
|
|
|
|
if (! $recording) {
|
|
return response()->json(['status' => 'not_found']);
|
|
}
|
|
|
|
$recordings->completeFromEgress($recording, $info);
|
|
|
|
return response()->json(['status' => 'accepted']);
|
|
}
|
|
}
|