Check if livekit is configured on the relay during room creation/edit

This commit is contained in:
mplorentz
2026-03-03 15:37:55 -05:00
parent 8b0ca5399d
commit f8ab970dc6
2 changed files with 52 additions and 7 deletions
+26 -2
View File
@@ -15,6 +15,7 @@
import ModalBody from "@lib/components/ModalBody.svelte"
import {pushToast} from "@app/util/toast"
import {uploadFile} from "@app/core/commands"
import {checkRelayHasLivekit} from "@app/voice"
type RoomMode = "text" | "voice" | "both"
@@ -46,10 +47,29 @@
const values = $state(initialValues)
let roomMode = $state<RoomMode>(getRoomModeFromEvent(initialValues.event))
let relayHasLivekit = $state<boolean | undefined>(undefined)
$effect(() => {
const u = url
let cancelled = false
checkRelayHasLivekit(u).then(has => {
if (!cancelled) relayHasLivekit = has
})
return () => {
cancelled = true
}
})
const submit = async () => {
const room = $state.snapshot(values)
if ((roomMode === "voice" || roomMode === "both") && !relayHasLivekit) {
return pushToast({
theme: "error",
message: "This relay does not support voice rooms.",
})
}
if (imageFile) {
const {error, result} = await uploadFile(imageFile, {
maxWidth: 256,
@@ -211,8 +231,12 @@
{#snippet input()}
<select class="select select-bordered w-full" bind:value={roomMode} aria-label="Room type">
<option value="text">Text only</option>
<option value="both">Text and voice</option>
<option value="voice">Voice only</option>
<option value="both" disabled={relayHasLivekit === false}>
Text and voice{relayHasLivekit === false ? " (not setup)" : ""}
</option>
<option value="voice" disabled={relayHasLivekit === false}>
Voice only{relayHasLivekit === false ? " (not setup)" : ""}
</option>
</select>
{/snippet}
</FieldInline>
+26 -5
View File
@@ -1,3 +1,7 @@
/**
* Voice rooms via LiveKit. Note: Voice does not work on localhost in Firefox
* (ICE candidate gathering fails). Use Chrome or test from deployed HTTPS.
*/
import {DisconnectReason, Room, RoomEvent, Track} from "livekit-client"
import {getToken} from "nostr-tools/nip98"
import {derived, get, writable} from "svelte/store"
@@ -9,6 +13,27 @@ import {pushToast} from "@app/util/toast"
export const ROOM_PRESENCE = 10312
const livekitEndpoint = (url: string, groupId: string) => {
const httpUrl = url
.replace(/^wss:\/\//, "https://")
.replace(/^ws:\/\//, "http://")
.replace(/\/$/, "")
return `${httpUrl}/.well-known/nip29/livekit/${groupId}`
}
export const checkRelayHasLivekit = async (url: string): Promise<boolean> => {
const endpoint = livekitEndpoint(url, "nop")
try {
// Currently we are hitting the API with no auth because zooid returns a 401 livekit
// is configured and 404 if it is not. But we need a standardized solution in the NIP.
const response = await fetch(endpoint)
return response.status === 401
} catch {
return false
}
}
const PRESENCE_INTERVAL_MS = 60_000
const PRESENCE_EXPIRY_S = 300
@@ -26,11 +51,7 @@ const fetchLivekitToken = async (
groupId: string,
signal?: AbortSignal,
): Promise<{server_url: string; participant_token: string}> => {
const httpUrl = url
.replace(/^wss:\/\//, "https://")
.replace(/^ws:\/\//, "http://")
.replace(/\/$/, "")
const endpoint = `${httpUrl}/.well-known/nip29/livekit/${groupId}`
const endpoint = livekitEndpoint(url, groupId)
const $signer = signer.get()
if (!$signer) throw new Error("No signer available")