вебкамера там, туда-сюда
All checks were successful
Deploy / publish-web (push) Successful in 1m16s

This commit is contained in:
2026-02-02 14:39:16 +06:00
parent 0922fc4f41
commit 269b19a5be
20 changed files with 546 additions and 211 deletions

View File

@@ -28,7 +28,7 @@
</div>
<div v-if="hasBadges" class="flex justify-end align-center gap-1 mt-2">
<PrimeBadge v-if="!!shareConsumer" v-tooltip.top="'Watch'" severity="success" value="Streaming" size="small" @click.stop="watchStream" />
<PrimeBadge v-if="streaming" v-tooltip.top="'Watch'" severity="success" value="Streaming" size="small" @click.stop="watchStream" />
<PrimeBadge v-if="premuted" severity="danger" value="Muted" size="small" />
<PrimeBadge v-else-if="client.outputMuted" severity="info" value="No sound" size="small" />
@@ -60,7 +60,6 @@
<script setup lang="ts">
import type { ChadClient } from '#shared/types'
import { useLocalStorage } from '@vueuse/core'
import { ChevronDown, ChevronUp, User } from 'lucide-vue-next'
import CollapseTransition from '~/components/CollapseTransition.vue'
@@ -69,44 +68,40 @@ const props = defineProps<{
}>()
const { outputMuted } = useApp()
const { consumers: allConsumers, micProducer, speakingClients } = useMediasoup()
const { consumers: allConsumers, micProducer } = useMediasoup()
const { me } = useClients()
const { show } = useFullscreenVideo()
const expanded = ref(false)
const volume = useLocalStorage<number>(computed(() => `CLIENT_VOLUME_${props.client.userId}`), 100, { writeDefaults: false })
const premuted = useLocalStorage<boolean>(computed(() => `CLIENT_PREMUTED_${props.client.userId}`), false, { writeDefaults: false })
const {
volume,
premuted,
speaking,
audioConsumers,
videoConsumers,
shareConsumers,
streaming,
} = useClient(toRef(() => props.client.socketId))
const isMe = computed(() => {
return me.value && props.client.userId === me.value.userId
})
const consumers = computed(() => {
return allConsumers.value.values().filter(consumer => consumer.appData.socketId === props.client.socketId).toArray()
})
const audioConsumer = computed(() => {
return consumers.value.find(consumer => consumer.track.kind === 'audio')
})
const videoConsumers = computed(() => {
return consumers.value.filter(consumer => consumer.track.kind === 'video')
})
const shareConsumer = computed(() => {
return videoConsumers.value.find(consumer => consumer.appData.source === 'share')
return audioConsumers.value[0]
})
const audioTrack = computed(() => {
return audioConsumer.value?.track
return audioConsumer.value?.raw.track
})
const speaking = computed(() => {
return speakingClients.value.find(speaker => speaker.clientId === props.client.socketId)
})
const audioConsumerPaused = computed(() => {
if (Object.keys(allConsumers.value).length === 0)
return false
const audioConsumerPaused = ref(false)
return audioConsumer.value?.paused ?? false
})
const inputMuted = computed(() => {
if (isMe.value)
@@ -116,17 +111,13 @@ const inputMuted = computed(() => {
})
const hasBadges = computed(() => {
return !!shareConsumer.value
return shareConsumers.value.length > 0
|| premuted.value
|| inputMuted.value
|| props.client.outputMuted
|| isMe.value
})
watch(allConsumers, () => {
audioConsumerPaused.value = audioConsumer.value?.paused ?? false
})
const { setGain } = useAudioContext(audioTrack)
watchEffect(() => {
@@ -141,9 +132,11 @@ function toggleExpand() {
}
function watchStream() {
if (!shareConsumer.value)
if (!streaming.value)
return
show(new MediaStream([shareConsumer.value.track]))
const consumer = [...videoConsumers.value, ...shareConsumers.value][0]!
show(new MediaStream([consumer.raw.track]))
}
</script>

View File

@@ -0,0 +1,25 @@
<template>
<div class="text-sm overflow-x-auto">
<p class="text-muted-color">
{{ consumer.id }}
</p>
<p>paused: {{ consumer.paused }}</p>
<p v-for="[key, value] in appData" :key="key">
{{ key }}: {{ value }}
</p>
</div>
</template>
<script setup lang="ts">
import type { Consumer } from 'mediasoup-client/types'
const props = defineProps<{
consumer: Consumer
}>()
const appData = computed(() => {
return Object.entries(props.consumer.appData)
})
</script>

View File

@@ -0,0 +1,20 @@
<template>
<Teleport to="body">
<div ref="root" class="fullscreen-gallery">
{{ videoConsumers.length + shareConsumers.length }}
</div>
</Teleport>
</template>
<script setup lang="ts">
import { useFullscreen } from '@vueuse/core'
const rootRef = useTemplateRef('root')
const { enter } = useFullscreen(rootRef)
const { videoConsumers, shareConsumers } = useMediasoup()
onMounted(() => {
// enter()
})
</script>

View File

@@ -0,0 +1,13 @@
<template>
<div class="fullscreen-gallery-card">
sasd
</div>
</template>
<script setup lang="ts">
</script>
<style>
</style>

View File

@@ -0,0 +1,21 @@
<template>
<div class="cursor-pointer hover:outline outline-primary rounded overflow-hidden flex items-center justify-center">
<video :srcObject="mediaStream" muted autoplay />
</div>
</template>
<script setup lang="ts">
import type { Consumer } from '#shared/types'
const props = defineProps<{
consumer: Consumer
}>()
const mediaStream = computed(() => {
return new MediaStream([props.consumer.raw.track])
})
</script>
<style>
</style>