Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

https://qall-microservice-for-livekit.trap.show/token?room=test をテストする用のPR #4470

Merged
merged 2 commits into from
Jan 23, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/composables/qall/useLiveKitSDK.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {

Check warning on line 1 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L1

Added line #L1 was not covered by tests
Track,
RoomEvent,
AudioPresets,
Expand All @@ -21,13 +21,13 @@
import { VirtualBackgroundProcessor } from '@shiguredo/virtual-background'
import mitt from 'mitt'

const virtualBackgroundAssetsPath =
'https://cdn.jsdelivr.net/npm/@shiguredo/virtual-background@latest/dist'

Check warning on line 25 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L24-L25

Added lines #L24 - L25 were not covered by tests
type QallEventMap = {
pushStamp: string
}
const { addErrorToast } = useToastStore()
const qallMitt = mitt<QallEventMap>()

Check warning on line 30 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L29-L30

Added lines #L29 - L30 were not covered by tests

export type TrackInfo = (
| {
Expand All @@ -48,422 +48,422 @@
backgroundImageSrc?: MediaStream
}

const room = ref<Room>()
const speakerIdentity = ref<string[]>([])
const tracksMap: Ref<Map<string, TrackInfo>> = ref(new Map())
const cameraProcessorMap: Ref<Map<string, CameraProcessor>> = ref(new Map())
const screenShareTrackSidMap = ref<Map<string, string>>(new Map())

Check warning on line 55 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L51-L55

Added lines #L51 - L55 were not covered by tests

function handleTrackSubscribed(
track: RemoteTrack,
publication: RemoteTrackPublication,
participant: RemoteParticipant
) {
if (track.kind === Track.Kind.Video || track.kind === Track.Kind.Audio) {

Check warning on line 62 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L57-L62

Added lines #L57 - L62 were not covered by tests
// attach it to a new HTMLVideoElement or HTMLAudioElement
tracksMap.value.set(publication.trackSid, {
isRemote: true,
trackPublication: publication,
participantIdentity: participant.identity
})
}
}

Check warning on line 70 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L64-L70

Added lines #L64 - L70 were not covered by tests

function handleTrackUnsubscribed(
track: RemoteTrack,
publication: RemoteTrackPublication,
participant: RemoteParticipant
) {

Check warning on line 76 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L72-L76

Added lines #L72 - L76 were not covered by tests
// remove tracks from all attached elements
tracksMap.value.delete(publication.trackSid)
screenShareTrackSidMap.value.delete(publication.trackSid)
}

Check warning on line 80 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L78-L80

Added lines #L78 - L80 were not covered by tests

function handleLocalTrackUnpublished(
publication: LocalTrackPublication,
participant: LocalParticipant
) {

Check warning on line 85 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L82-L85

Added lines #L82 - L85 were not covered by tests
// when local tracks are ended, update UI to remove them from rendering
tracksMap.value.delete(publication.trackSid)
screenShareTrackSidMap.value.delete(publication.trackSid)
}

Check warning on line 89 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L87-L89

Added lines #L87 - L89 were not covered by tests

function handleLocalTrackPublished(
publication: LocalTrackPublication,
participant: LocalParticipant
) {

Check warning on line 94 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L91-L94

Added lines #L91 - L94 were not covered by tests
// when local tracks are ended, update UI to remove them from rendering
if (!publication.track) return
tracksMap.value.set(publication.trackSid, {
isRemote: false,
trackPublication: publication,
participantIdentity: participant.identity
})
}

Check warning on line 102 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L96-L102

Added lines #L96 - L102 were not covered by tests

function handleActiveSpeakerChange(speakers: Participant[]) {

Check warning on line 104 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L104

Added line #L104 was not covered by tests
// show UI indicators when participant is speaking
speakerIdentity.value = speakers.map(s => s.identity)
}

Check warning on line 107 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L106-L107

Added lines #L106 - L107 were not covered by tests

function handleDisconnect() {

Check warning on line 109 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L109

Added line #L109 was not covered by tests
//
}

Check warning on line 111 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L111

Added line #L111 was not covered by tests

function handleDataReceived(payload: Uint8Array<ArrayBufferLike>) {
const data = JSON.parse(decoder.decode(payload))
if (!data.type || !data.message) return
if (data.type === 'stamp') {
qallMitt.emit('pushStamp', data.message)
}
}

Check warning on line 119 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L113-L119

Added lines #L113 - L119 were not covered by tests

function handleParticipantAttributesChanged(
changed: Record<string, string>,
participant: Participant
) {
Object.keys(changed).forEach(key =>
screenShareTrackSidMap.value.set(key, changed[key] ?? '')
)
}

Check warning on line 128 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L121-L128

Added lines #L121 - L128 were not covered by tests

const joinRoom = async (roomName: string, userName: string) => {
try {
const traQtoken = (await apis.getMyQRCode(true)).data
const res = await fetch(
`https://easy-livekit-token-publisher.trap.show/token`,
`https://qall-microservice-for-livekit.trap.show/api/token?room=test`,
{
method: 'GET',
headers: {
Authorization: `Bearer ${traQtoken}`,
'Content-Type': 'application/json'
}
}
)
const json = await res.json()
const token = json.token

Check warning on line 144 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L130-L144

Added lines #L130 - L144 were not covered by tests

// pre-warm connection, this can be called as early as your page is loaded
//room.prepareConnection("https://livekit-test.trap.show:39357", token);
room.value = new Room({ dynacast: true, adaptiveStream: true })
await room.value.prepareConnection(
'wss://livekit.qall-dev.trapti.tech',
token
)

Check warning on line 152 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L148-L152

Added lines #L148 - L152 were not covered by tests

// set up event listeners
room.value
.on(RoomEvent.TrackSubscribed, handleTrackSubscribed)
.on(RoomEvent.TrackUnsubscribed, handleTrackUnsubscribed)
.on(RoomEvent.ActiveSpeakersChanged, handleActiveSpeakerChange)
.on(RoomEvent.Disconnected, handleDisconnect)
.on(RoomEvent.LocalTrackUnpublished, handleLocalTrackUnpublished)
.on(RoomEvent.LocalTrackPublished, handleLocalTrackPublished)
.on(RoomEvent.DataReceived, handleDataReceived)
.on(
RoomEvent.ParticipantAttributesChanged,
handleParticipantAttributesChanged
)

Check warning on line 166 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L155-L166

Added lines #L155 - L166 were not covered by tests

// connect to room
await room.value.connect('wss://livekit.qall-dev.trapti.tech', token)

Check warning on line 169 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L169

Added line #L169 was not covered by tests

// publish local camera and mic tracks
await room.value.localParticipant.setMicrophoneEnabled(
true,
{
channelCount: 2,
voiceIsolation: true,
echoCancellation: true,
noiseSuppression: true
},
{
audioPreset: AudioPresets.musicHighQualityStereo,
forceStereo: true,
red: false,
dtx: false
}
)
await room.value.localParticipant.setAttributes({})
room.value.remoteParticipants.forEach(participant => {
Object.keys(participant.attributes).forEach(key =>
screenShareTrackSidMap.value.set(key, participant.attributes[key] ?? '')
)
})
} catch {
addErrorToast('Qallの接続に失敗しました')
await leaveRoom()
}

Check warning on line 196 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L172-L196

Added lines #L172 - L196 were not covered by tests

window.addEventListener('beforeunload', leaveRoom)
}

Check warning on line 199 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L198-L199

Added lines #L198 - L199 were not covered by tests

async function leaveRoom() {

Check warning on line 201 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L201

Added line #L201 was not covered by tests
// Leave the room by calling 'disconnect' method over the Room object
await room.value?.disconnect()

Check warning on line 203 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L203

Added line #L203 was not covered by tests

// Empty all variables
room.value = undefined
tracksMap.value.clear()
screenShareTrackSidMap.value.clear()

Check warning on line 208 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L206-L208

Added lines #L206 - L208 were not covered by tests

window.removeEventListener('beforeunload', leaveRoom)
}

Check warning on line 211 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L210-L211

Added lines #L210 - L211 were not covered by tests

const Attributes = ref<{ [key: string]: string }>({})

Check warning on line 213 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L213

Added line #L213 was not covered by tests

const addCameraTrack = async (
videoInputDevice?: MediaDeviceInfo,
backgroundType?: 'original' | 'blur' | 'file' | 'screen',
backgroundFile?: File
) => {
try {
if (!room.value) {
addErrorToast('ルームが存在しません')
return
}

Check warning on line 224 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L215-L224

Added lines #L215 - L224 were not covered by tests

const stream = await navigator.mediaDevices.getUserMedia({
video: { deviceId: videoInputDevice?.deviceId }
})
const track = stream.getVideoTracks()[0]
if (!track) {
addErrorToast('映像が取得できませんでした')
return
}
let backgroundImageSrc: MediaStream | undefined
const processor = new VirtualBackgroundProcessor(
virtualBackgroundAssetsPath
)
let options = {}

Check warning on line 238 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L226-L238

Added lines #L226 - L238 were not covered by tests

if (backgroundType === 'blur') {
options = {
blurRadius: 10
}
} else if (backgroundType === 'file' && backgroundFile) {
const blob = new Blob([backgroundFile], { type: backgroundFile.type })

Check warning on line 245 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L240-L245

Added lines #L240 - L245 were not covered by tests

if (backgroundFile.type.startsWith('image/')) {

Check warning on line 247 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L247

Added line #L247 was not covered by tests
//画像のとき
const imageBitmap = await createImageBitmap(blob)
options = {
backgroundImage: imageBitmap
}
} else if (backgroundFile.type.startsWith('video/')) {

Check warning on line 253 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L249-L253

Added lines #L249 - L253 were not covered by tests
//動画のとき
const videoElement = await new Promise<HTMLVideoElement>(resolve => {
const video = document.createElement('video')
video.addEventListener('loadedmetadata', () => {
video.play()
resolve(video)
})
video.src = URL.createObjectURL(blob)
video.muted = true
video.loop = true
})
const canvas = drawVideoToCanvas(videoElement)
options = {
backgroundImage: canvas
}
}
} else if (backgroundType === 'screen') {
backgroundImageSrc = await navigator.mediaDevices.getDisplayMedia({
video: true
})
const videoElement = await new Promise<HTMLVideoElement>(resolve => {
const video = document.createElement('video')
video.addEventListener('loadedmetadata', () => {
video.play()
resolve(video)
})
if (backgroundImageSrc) {
video.srcObject = backgroundImageSrc
video.autoplay = true
video.muted = true
}
})

Check warning on line 285 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L255-L285

Added lines #L255 - L285 were not covered by tests

const canvas = drawVideoToCanvas(videoElement)
options = {
backgroundImage: canvas
}
}
const processedTrack = await processor.startProcessing(track, options)
const localTrack = new LocalVideoTrack(processedTrack)
await room.value?.localParticipant
.publishTrack(localTrack, { simulcast: true })
.catch(() => {
addErrorToast('カメラの共有に失敗しました')
track.stop()
processor.stopProcessing()
return
})
if (localTrack.sid) {
cameraProcessorMap.value.set(localTrack.sid, {
processor,
track,
backgroundImageSrc
})
}
} catch (e) {

Check warning on line 309 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L287-L309

Added lines #L287 - L309 were not covered by tests
// eslint-disable-next-line no-console
console.error(e)
addErrorToast('カメラの共有に失敗しました')
return
}
}

Check warning on line 315 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L311-L315

Added lines #L311 - L315 were not covered by tests

const drawVideoToCanvas = (video: HTMLVideoElement) => {
const canvas = new OffscreenCanvas(video.videoWidth, video.videoHeight)
const canvasCtx = canvas.getContext('2d', {
desynchronized: true
})
if (!canvasCtx) return
const updateCanvas = () => {
canvasCtx.drawImage(video, 0, 0)
requestAnimationFrame(updateCanvas)
}
updateCanvas()
return canvas
}

Check warning on line 329 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L317-L329

Added lines #L317 - L329 were not covered by tests

const addScreenShareTrack = async () => {
try {
if (!room.value) {
addErrorToast('ルームが存在しません')
return
}
const localTracks = await createLocalScreenTracks({
audio: {
channelCount: 2,
autoGainControl: false,
noiseSuppression: false,
echoCancellation: false,
voiceIsolation: false
}
})

Check warning on line 345 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L331-L345

Added lines #L331 - L345 were not covered by tests

await Promise.all(
localTracks.map(async t => {
if (t.kind === Track.Kind.Video) {
await room.value?.localParticipant.publishTrack(t, {
simulcast: true
})
} else {
await room.value?.localParticipant.publishTrack(t, {
audioPreset: AudioPresets.musicHighQualityStereo,
dtx: false,
red: false
})
}
})
)
const videoSid = localTracks.find(t => t.kind === Track.Kind.Video)?.sid
const audioSid = localTracks.find(t => t.kind === Track.Kind.Audio)?.sid
if (audioSid && videoSid) {
Attributes.value = {
...Attributes.value,
[videoSid]: audioSid
}
screenShareTrackSidMap.value.set(videoSid, audioSid)

Check warning on line 369 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L347-L369

Added lines #L347 - L369 were not covered by tests
// await room.value.localParticipant.setAttributes({
// ...room.value.localParticipant.attributes,
// [videoSid]: audioSid
// })
}
} catch {

Check warning on line 375 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L374-L375

Added lines #L374 - L375 were not covered by tests
// TODO:シェアをキャンセルした時も失敗しましたメッセージがでるのはちょっと違和感があるかも
addErrorToast('スクリーン共有に失敗しました')
throw new Error('スクリーン共有に失敗しました')
}
}

Check warning on line 380 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L377-L380

Added lines #L377 - L380 were not covered by tests

const removeVideoTrack = async (localpublication: LocalTrackPublication) => {
if (localpublication.track) {
if (!room.value) {
addErrorToast('ルームが存在しません')
return
}
const cameraProcessor = cameraProcessorMap.value.get(
localpublication.trackSid
)
if (cameraProcessor) {
cameraProcessor.processor.stopProcessing()
cameraProcessor.track.stop()
cameraProcessor.backgroundImageSrc
?.getTracks()
.forEach(track => track.stop())
cameraProcessorMap.value.delete(localpublication.trackSid)
}

Check warning on line 398 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L382-L398

Added lines #L382 - L398 were not covered by tests

const { [localpublication.trackSid]: audioSid, ...newAttributes } =
Attributes.value

Check warning on line 401 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L400-L401

Added lines #L400 - L401 were not covered by tests
//room.value.localParticipant.attributes
await room.value.localParticipant.unpublishTrack(
localpublication.track,
true
)
room.value.localParticipant.setAttributes(newAttributes)
Attributes.value = newAttributes
if (!audioSid) {
return
}

Check warning on line 411 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L403-L411

Added lines #L403 - L411 were not covered by tests

const audioTrack = tracksMap.value.get(audioSid)
if (
!audioTrack ||
audioTrack.isRemote ||
!audioTrack.trackPublication?.track
) {
return
}

Check warning on line 420 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L413-L420

Added lines #L413 - L420 were not covered by tests

await room.value.localParticipant.unpublishTrack(
audioTrack.trackPublication.track,
true
)
}
}

Check warning on line 427 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L422-L427

Added lines #L422 - L427 were not covered by tests

const setLocalTrackMute = async (track: LocalTrack, muted: boolean) => {
if (muted) {
await track.mute()
} else {
await track.unmute()
}
}

Check warning on line 435 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L429-L435

Added lines #L429 - L435 were not covered by tests

const setTrackEnabled = (
publication: RemoteTrackPublication,
muted: boolean
) => {
publication.setEnabled(!muted)
}

Check warning on line 442 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L437-L442

Added lines #L437 - L442 were not covered by tests

const publishData = async (data: { type: 'stamp'; message: string }) => {
if (!room.value) return
const strData = JSON.stringify(data)
const encoder = new TextEncoder()

Check warning on line 447 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L444-L447

Added lines #L444 - L447 were not covered by tests

// publishData takes in a Uint8Array, so we need to convert it
const encoded = encoder.encode(strData)
await room.value.localParticipant.publishData(encoded, { reliable: true })
}
const decoder = new TextDecoder()

Check warning on line 453 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L450-L453

Added lines #L450 - L453 were not covered by tests

export const useLiveKitSDK = () => {
return {
joinRoom,
leaveRoom,
addScreenShareTrack,
addCameraTrack,
removeVideoTrack,
publishData,
setTrackEnabled,
setLocalTrackMute,
tracksMap,
screenShareTrackSidMap,
qallMitt
}
}

Check warning on line 469 in src/composables/qall/useLiveKitSDK.ts

View check run for this annotation

Codecov / codecov/patch

src/composables/qall/useLiveKitSDK.ts#L455-L469

Added lines #L455 - L469 were not covered by tests
Loading