Fix LiveKit connect flow and polish meeting room media UI.
Deploy Ladill Meet / deploy (push) Successful in 1m2s

Use the official LiveKit PHP SDK for tokens, harden client connect/publish timing, keep the Room outside Alpine reactivity, and replace emoji toolbar controls with synced mic/camera toggles and avatar tiles for audio-only participants.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-01 07:55:13 +00:00
co-authored by Cursor
parent 3996d27bca
commit 8ffb64191f
5 changed files with 738 additions and 316 deletions
+23 -37
View File
@@ -2,6 +2,10 @@
namespace App\Services\Meet\Media;
use Agence104\LiveKit\AccessToken;
use Agence104\LiveKit\AccessTokenOptions;
use Agence104\LiveKit\VideoGrant;
class LiveKitProvider implements MediaProviderInterface
{
public function createRoom(string $roomName, array $options = []): void
@@ -23,25 +27,26 @@ class LiveKitProvider implements MediaProviderInterface
$apiKey = (string) config('meet.media.livekit.api_key');
$apiSecret = (string) config('meet.media.livekit.api_secret');
$now = time();
$payload = [
'iss' => $apiKey,
'sub' => $identity,
'iat' => $now,
'nbf' => $now,
'exp' => $now + 3600,
'name' => $displayName,
'video' => [
'roomJoin' => true,
'room' => $roomName,
'canPublish' => $grants['can_publish'] ?? true,
'canSubscribe' => $grants['can_subscribe'] ?? true,
'canPublishData' => $grants['can_publish_data'] ?? true,
'roomAdmin' => $grants['room_admin'] ?? false,
],
];
$tokenOptions = (new AccessTokenOptions())
->setIdentity($identity)
->setName($displayName)
->setTtl(3600);
return $this->encodeJwt($payload, $apiSecret);
$videoGrant = (new VideoGrant())
->setRoomJoin()
->setRoomName($roomName)
->setCanPublish($grants['can_publish'] ?? true)
->setCanSubscribe($grants['can_subscribe'] ?? true)
->setCanPublishData($grants['can_publish_data'] ?? true);
if ($grants['room_admin'] ?? false) {
$videoGrant->setRoomAdmin();
}
return (new AccessToken($apiKey, $apiSecret))
->init($tokenOptions)
->setGrant($videoGrant)
->toJwt();
}
public function isConfigured(): bool
@@ -55,23 +60,4 @@ class LiveKitProvider implements MediaProviderInterface
{
return (string) config('meet.media.livekit.url');
}
/**
* @param array<string, mixed> $payload
*/
protected function encodeJwt(array $payload, string $secret): string
{
$header = $this->base64UrlEncode(json_encode(['alg' => 'HS256', 'typ' => 'JWT'], JSON_THROW_ON_ERROR));
$body = $this->base64UrlEncode(json_encode($payload, JSON_THROW_ON_ERROR));
$signature = $this->base64UrlEncode(
hash_hmac('sha256', "{$header}.{$body}", $secret, true)
);
return "{$header}.{$body}.{$signature}";
}
protected function base64UrlEncode(string $data): string
{
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
}