cringe sfx

This commit is contained in:
2026-04-12 22:35:47 +06:00
parent 7b2cfb4b56
commit 92f7bae50d
15 changed files with 700 additions and 597 deletions

View File

@@ -1,49 +1,21 @@
import type { types } from 'mediasoup'
import type { ActiveSpeakerObserver, AudioLevelObserver } from 'mediasoup/types'
import type { Server as SocketServer } from 'socket.io'
import type {
ChadClient,
ChadSocket,
SomeSocket,
} from '../types/socket.ts'
import { consola } from 'consola'
import prisma from '../prisma/client.ts'
import { socketToClient } from '../utils/socket-to-client.ts'
export default async function (io: SocketServer, router: types.Router) {
const audioLevelObserver = await router.createAudioLevelObserver({
maxEntries: 10,
threshold: -80,
interval: 800,
})
const activeSpeakerObserver = await router.createActiveSpeakerObserver()
audioLevelObserver.on('volumes', async (volumes: types.AudioLevelObserverVolume[]) => {
io.emit('speakingPeers', volumes.map(({ producer, volume }) => {
const { socketId } = producer.appData as { socketId: ChadClient['socketId'] }
return {
clientId: socketId,
volume,
}
}))
})
audioLevelObserver.on('silence', () => {
io.emit('speakingPeers', [])
io.emit('activeSpeaker', undefined)
})
activeSpeakerObserver.on('dominantspeaker', ({ producer }) => {
const { socketId } = producer.appData as { socketId: ChadClient['socketId'] }
io.emit('activeSpeaker', socketId)
})
export default async function (
io: SocketServer,
socket: ChadSocket,
router: types.Router,
audioLevelObserver: AudioLevelObserver,
activeSpeakerObserver: ActiveSpeakerObserver,
) {
io.on('connection', async (socket) => {
consola.info('[WebRtc]', 'Client connected', socket.id)
socket.data.joined = false
socket.data.inputMuted = false
socket.data.outputMuted = false
@@ -51,62 +23,11 @@ export default async function (io: SocketServer, router: types.Router) {
socket.data.producers = new Map()
socket.data.consumers = new Map()
const user = await prisma.user.findUnique({
where: {
id: socket.handshake.auth.userId,
},
select: {
id: true,
username: true,
displayName: true,
},
})
if (!user) {
socket.disconnect()
return
}
const { id, username, displayName } = user
socket.data.userId = id
socket.data.username = username
socket.data.displayName = displayName
socket.emit('authenticated', { channels })
socket.on('join', async ({ rtpCapabilities }, cb) => {
if (socket.data.joined) {
consola.error('[WebRtc]', 'Already joined')
cb({ error: 'Already joined' })
}
socket.data.joined = true
socket.data.rtpCapabilities = rtpCapabilities
const joinedSockets = await getJoinedSockets()
cb(joinedSockets.map(socketToClient))
for (const joinedSocket of joinedSockets.filter(joinedSocket => joinedSocket.id !== socket.id)) {
for (const producer of joinedSocket.data.producers.values()) {
createConsumer(
socket,
joinedSocket,
producer,
)
}
}
socket.broadcast.emit('newPeer', socketToClient(socket))
})
socket.on('getRtpCapabilities', (cb) => {
socket.on('webrtc:get-rtp-capabilities', (cb) => {
cb(router.rtpCapabilities)
})
socket.on('createTransport', async ({ producing, consuming }, cb) => {
socket.on('webrtc:create-transport', async ({ producing, consuming }, cb) => {
try {
const transport = await router.createWebRtcTransport({
listenInfos: [
@@ -161,7 +82,7 @@ export default async function (io: SocketServer, router: types.Router) {
}
})
socket.on('connectTransport', async ({ transportId, dtlsParameters }, cb) => {
socket.on('webrtc:connect-transport', async ({ transportId, dtlsParameters }, cb) => {
const transport = socket.data.transports.get(transportId)
if (!transport) {
@@ -184,7 +105,7 @@ export default async function (io: SocketServer, router: types.Router) {
}
})
socket.on('produce', async ({ transportId, kind, rtpParameters, appData }, cb) => {
socket.on('webrtc:produce', async ({ transportId, kind, rtpParameters, appData }, cb) => {
if (!socket.data.joined) {
consola.error('Peer not joined yet')
cb({ error: 'Peer not joined yet' })
@@ -192,6 +113,14 @@ export default async function (io: SocketServer, router: types.Router) {
return
}
// Block production in default channel
const currentChannelId = Array.from(socket.rooms).find(room => room !== socket.id) || 'default'
if (currentChannelId === 'default') {
consola.error('Cannot produce in default channel')
cb({ error: 'Cannot produce media in default channel' })
return
}
const transport = socket.data.transports.get(transportId)
if (!transport) {
@@ -208,7 +137,8 @@ export default async function (io: SocketServer, router: types.Router) {
cb({ id: producer.id })
const otherSockets = await getJoinedSockets(socket.id)
// Filter by channel when creating consumers
const otherSockets = await getJoinedSockets(socket.id, currentChannelId)
for (const otherSocket of otherSockets) {
createConsumer(
@@ -229,7 +159,7 @@ export default async function (io: SocketServer, router: types.Router) {
}
})
socket.on('closeProducer', async ({ producerId }, cb) => {
socket.on('webrtc:close-producer', async ({ producerId }, cb) => {
if (!socket.data.joined) {
consola.error('Peer not joined yet')
cb({ error: 'Peer not joined yet' })
@@ -253,7 +183,7 @@ export default async function (io: SocketServer, router: types.Router) {
cb({ ok: true })
})
socket.on('pauseProducer', async ({ producerId }, cb) => {
socket.on('webrtc:pause-producer', async ({ producerId }, cb) => {
if (!socket.data.joined) {
consola.error('Peer not joined yet')
cb({ error: 'Peer not joined yet' })
@@ -278,7 +208,7 @@ export default async function (io: SocketServer, router: types.Router) {
cb({ ok: true })
})
socket.on('resumeProducer', async ({ producerId }, cb) => {
socket.on('webrtc:resume-producer', async ({ producerId }, cb) => {
if (!socket.data.joined) {
consola.error('Peer not joined yet')
cb({ error: 'Peer not joined yet' })
@@ -300,7 +230,7 @@ export default async function (io: SocketServer, router: types.Router) {
cb({ ok: true })
})
socket.on('pauseConsumer', async ({ consumerId }, cb) => {
socket.on('webrtc:pause-consumer', async ({ consumerId }, cb) => {
if (!socket.data.joined) {
consola.error('Peer not joined yet')
cb({ error: 'Peer not joined yet' })
@@ -322,7 +252,7 @@ export default async function (io: SocketServer, router: types.Router) {
cb({ ok: true })
})
socket.on('resumeConsumer', async ({ consumerId }, cb) => {
socket.on('webrtc:resume-consumer', async ({ consumerId }, cb) => {
if (!socket.data.joined) {
consola.error('Peer not joined yet')
cb({ error: 'Peer not joined yet' })
@@ -344,7 +274,7 @@ export default async function (io: SocketServer, router: types.Router) {
cb({ ok: true })
})
socket.on('updateClient', async (updatedClient, cb) => {
socket.on('webrtc:update-client', async (updatedClient, cb) => {
if (typeof updatedClient.inputMuted === 'boolean') {
socket.data.inputMuted = updatedClient.inputMuted
}
@@ -355,14 +285,21 @@ export default async function (io: SocketServer, router: types.Router) {
cb(socketToClient(socket))
io.emit('clientChanged', socket.id, socketToClient(socket))
io.emit('webrtc:client-changed', socket.id, socketToClient(socket))
})
socket.on('disconnect', () => {
consola.info('Client disconnected:', socket.id)
// Get current channel from Socket.IO rooms
const channelId = Array.from(socket.rooms).find(room => room !== socket.id)
if (socket.data.joined) {
socket.broadcast.emit('peerClosed', socket.id)
// Notify only same channel using Socket.IO room
if (channelId) {
socket.to(channelId).emit('webrtc:peer-closed', socket.id)
io.emit('channelUserLeft', { channelId, clientId: socket.id })
}
}
for (const transport of socket.data.transports.values()) {
@@ -371,10 +308,21 @@ export default async function (io: SocketServer, router: types.Router) {
})
})
async function getJoinedSockets(excludeId?: string) {
const sockets = await io.fetchSockets()
async function getJoinedSockets(excludeId?: string, channelId?: string) {
let sockets = await io.fetchSockets()
return sockets.filter(socket => socket.data.joined && (excludeId ? excludeId !== socket.id : true))
// Filter by channel using Socket.IO rooms
if (channelId) {
sockets = await io.in(channelId).fetchSockets()
}
return sockets.filter((socket) => {
if (!socket.data.joined)
return false
if (excludeId && socket.id === excludeId)
return false
return true
})
}
async function createConsumer(
@@ -432,24 +380,24 @@ export default async function (io: SocketServer, router: types.Router) {
consumer.on('producerclose', () => {
consumerSocket.data.consumers.delete(consumer.id)
consumerSocket.emit('consumerClosed', { consumerId: consumer.id })
consumerSocket.emit('webrtc:consumer-closed', { consumerId: consumer.id })
})
consumer.on('producerpause', () => {
consumerSocket.emit('consumerPaused', { consumerId: consumer.id })
consumerSocket.emit('webrtc:consumer-paused', { consumerId: consumer.id })
})
consumer.on('producerresume', () => {
consumerSocket.emit('consumerResumed', { consumerId: consumer.id })
consumerSocket.emit('webrtc:consumer-resumed', { consumerId: consumer.id })
})
consumer.on('score', (score: types.ConsumerScore) => {
consumerSocket.emit('consumerScore', { consumerId: consumer.id, score })
consumerSocket.emit('webrtc:consumer-score', { consumerId: consumer.id, score })
})
try {
await consumerSocket.emitWithAck(
'newConsumer',
'webrtc:new-consumer',
{
socketId: producerSocket.id,
producerId: producer.id,
@@ -465,7 +413,7 @@ export default async function (io: SocketServer, router: types.Router) {
await consumer.resume()
consumerSocket.emit(
'consumerScore',
'webrtc:consumer-score',
{
consumerId: consumer.id,
score: consumer.score,