stream volume and listener count
							parent
							
								
									3b288c9719
								
							
						
					
					
						commit
						4769e418e8
					
				
							
								
								
									
										3
									
								
								DOCS.md
								
								
								
								
							
							
						
						
									
										3
									
								
								DOCS.md
								
								
								
								
							|  | @ -1,3 +1,6 @@ | ||||||
|  | # NOTICE: this document is outdated | ||||||
|  | # todo: update this document | ||||||
|  | 
 | ||||||
| # vybe websocket protocol - sent by client | # vybe websocket protocol - sent by client | ||||||
| 
 | 
 | ||||||
| socket.io actions + expected msg format and other info | socket.io actions + expected msg format and other info | ||||||
|  |  | ||||||
|  | @ -46,25 +46,61 @@ | ||||||
| 				background: #303030; | 				background: #303030; | ||||||
| 			} | 			} | ||||||
| 			button, | 			button, | ||||||
| 			input, | 			input:not([type]), input[type='text'], | ||||||
| 			.tab { | 			.tab { | ||||||
| 				padding: 4px 7px; | 				padding: 4px 7px; | ||||||
| 			} | 			} | ||||||
| 			input { | 			input { | ||||||
| 				background: #1b1b1b; | 				background: #1b1b1b; | ||||||
| 				outline: none; | 				outline: none; | ||||||
| 				border: 1px solid #444; |  | ||||||
| 				&:focus { |  | ||||||
| 					padding-bottom: 2px; |  | ||||||
| 					border-bottom: 3px solid #777; |  | ||||||
| 				} |  | ||||||
| 				&::placeholder { | 				&::placeholder { | ||||||
| 					color: #aaa; | 					color: #aaa; | ||||||
| 				} | 				} | ||||||
|  | 				&:not([type]), &[type='text'] { | ||||||
|  | 					border: 1px solid #444; | ||||||
|  | 					&:focus { | ||||||
|  | 						padding-bottom: 2px; | ||||||
|  | 						border-bottom: 3px solid #777; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
| 				&[type='radio'], &[type='checkbox'] { | 				&[type='radio'], &[type='checkbox'] { | ||||||
| 					position: relative; | 					position: relative; | ||||||
| 					top: 2px; | 					top: 2px; | ||||||
| 				} | 				} | ||||||
|  | 				&[type='range'] { | ||||||
|  | 					width: 100px; | ||||||
|  | 					-webkit-appearance: none; | ||||||
|  | 					appearance: none; | ||||||
|  | 					background-color: #4f4f4f; | ||||||
|  | 					border-radius: 1rem; | ||||||
|  | 					height: 0.5rem; | ||||||
|  | 					&:focus { | ||||||
|  | 						outline: none; | ||||||
|  | 					} | ||||||
|  | 					&::-webkit-slider-thumb { | ||||||
|  | 						-webkit-appearance: none; | ||||||
|  | 						appearance: none; | ||||||
|  | 						background-color: #555; | ||||||
|  | 						border-radius: 1rem; | ||||||
|  | 						border: 2px solid #909090; | ||||||
|  | 						height: 17px; | ||||||
|  | 						width: 17px; | ||||||
|  | 					} | ||||||
|  | 					&:focus::-webkit-slider-thumb { | ||||||
|  | 						border: 2px solid #e0e0e0; | ||||||
|  | 					} | ||||||
|  | 					&::-moz-range-thumb { | ||||||
|  | 						appearance: none; | ||||||
|  | 						background-color: #555; | ||||||
|  | 						border-radius: 1rem; | ||||||
|  | 						border: 2px solid #909090; | ||||||
|  | 						height: 13px; | ||||||
|  | 						width: 13px; | ||||||
|  | 					} | ||||||
|  | 					&:focus::-moz-range-thumb { | ||||||
|  | 						border: 2px solid #e0e0e0; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 			#register { | 			#register { | ||||||
| 				margin-inline: 14px; | 				margin-inline: 14px; | ||||||
|  | @ -91,7 +127,7 @@ | ||||||
| 				display: block; | 				display: block; | ||||||
| 			} | 			} | ||||||
| 			h3 { | 			h3 { | ||||||
| 				margin: 0; | 				margin: 2px; | ||||||
| 			} | 			} | ||||||
| 			h4 { | 			h4 { | ||||||
| 				margin: 6px 0; | 				margin: 6px 0; | ||||||
|  | @ -142,11 +178,14 @@ | ||||||
| 			} | 			} | ||||||
| 			#home { | 			#home { | ||||||
| 				margin: 0; | 				margin: 0; | ||||||
| 				max-width: 250px; | 				max-width: 256px; | ||||||
| 				display: flex; | 				display: flex; | ||||||
| 				flex-direction: column; | 				flex-direction: column; | ||||||
| 				justify-content: space-between; | 				justify-content: space-between; | ||||||
| 			} | 			} | ||||||
|  | 			#instances { | ||||||
|  | 				margin-inline: 2px; | ||||||
|  | 			} | ||||||
| 			#instancelist { | 			#instancelist { | ||||||
| 				overflow-y: auto; | 				overflow-y: auto; | ||||||
| 				> :not(div) { | 				> :not(div) { | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ async function stream() { | ||||||
| 			stop: true | 			stop: true | ||||||
| 		}); | 		}); | ||||||
| 		document.getElementById('streaming').innerText = 'start streaming'; | 		document.getElementById('streaming').innerText = 'start streaming'; | ||||||
|  | 		document.getElementById('listeners').innerText = ''; | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 	if (!mediaStream) | 	if (!mediaStream) | ||||||
|  | @ -71,8 +72,6 @@ async function stream() { | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| let audioctx; |  | ||||||
| 
 |  | ||||||
| function loadStreams() { | function loadStreams() { | ||||||
| 	let instance = window.currentInstance; | 	let instance = window.currentInstance; | ||||||
| 
 | 
 | ||||||
|  | @ -93,7 +92,10 @@ function loadStreams() { | ||||||
| 						name: this.value | 						name: this.value | ||||||
| 					}); | 					}); | ||||||
| 				window.currentThread.streamname = this.value; | 				window.currentThread.streamname = this.value; | ||||||
| 			}}>`);
 | 			}}> | ||||||
|  | 			<p id='listeners'>${window.currentThread.listeners ? | ||||||
|  | 				window.currentThread.listeners + ' listeners' : ''} | ||||||
|  | 			</p>`); | ||||||
| 		if (window.currentThread.streamname) | 		if (window.currentThread.streamname) | ||||||
| 			document.getElementById('streamname').value = window.currentThread.streamname; | 			document.getElementById('streamname').value = window.currentThread.streamname; | ||||||
| 	} | 	} | ||||||
|  | @ -104,18 +106,21 @@ function loadStreams() { | ||||||
| 	function addStream(stream) { | 	function addStream(stream) { | ||||||
| 		let p = html.node` | 		let p = html.node` | ||||||
| 			<p> | 			<p> | ||||||
| 				<button id='play' onclick=${e => { | 				<button id='play' onclick=${function(event) { | ||||||
| 					if (stream.playing) { | 					if (stream.playing) { | ||||||
| 						audioctx.suspend(); | 						stream.audioctx.suspend(); | ||||||
| 						delete instance.streaming[stream.id]; | 						delete instance.streaming[stream.id]; | ||||||
| 						stream.playing = false; | 						stream.playing = false; | ||||||
| 						e.target.innerText = '▶'; | 						this.innerText = '▶'; | ||||||
| 					} | 					} | ||||||
| 					else { | 					else { | ||||||
| 						audioctx = new AudioContext(); | 						stream.audioctx = new AudioContext(); | ||||||
|  | 						stream.gain = stream.audioctx.createGain(); | ||||||
|  | 						stream.gain.connect(stream.audioctx.destination); | ||||||
|  | 						stream.gain.gain.value = p.children['volume'].value / 100; | ||||||
| 						instance.streaming[stream.id] = stream; | 						instance.streaming[stream.id] = stream; | ||||||
| 						stream.playing = true; | 						stream.playing = true; | ||||||
| 						e.target.innerText = '⏹'; | 						this.innerText = '⏹'; | ||||||
| 					} | 					} | ||||||
| 					instance.emit('play_stream', { | 					instance.emit('play_stream', { | ||||||
| 						id: stream.id, | 						id: stream.id, | ||||||
|  | @ -126,11 +131,15 @@ function loadStreams() { | ||||||
| 							console.log('play stream failed: ', msg.message); | 							console.log('play stream failed: ', msg.message); | ||||||
| 					}); | 					}); | ||||||
| 				}}>${instance.streaming[stream.id] ? '⏹' : '▶'}</button> | 				}}>${instance.streaming[stream.id] ? '⏹' : '▶'}</button> | ||||||
|  | 				<input id='volume' type='range' oninput=${function(event) { | ||||||
|  | 					if (stream.gain) | ||||||
|  | 						stream.gain.gain.value = this.value / 100; | ||||||
|  | 				}}> | ||||||
| 				<span id='name'>${stream.name ? ` - ${stream.name}` : ''}</span> | 				<span id='name'>${stream.name ? ` - ${stream.name}` : ''}</span> | ||||||
| 			</p>`; | 			</p>`; | ||||||
| 		p.insertBefore(window.makeUser(stream.user, | 		p.insertBefore(window.makeUser(stream.user, | ||||||
| 			stream.user.id.split?.('@')[1] || window.currentInstance.url), | 			stream.user.id.split?.('@')[1] || window.currentInstance.url), | ||||||
| 			p.children[1]); | 			p.children[2]); | ||||||
| 		p.id = 'stream' + stream.id; | 		p.id = 'stream' + stream.id; | ||||||
| 		document.getElementById('streams').append(p); | 		document.getElementById('streams').append(p); | ||||||
| 	} | 	} | ||||||
|  | @ -164,13 +173,23 @@ function loadStreams() { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		instance.socket.on('streamdata', async msg => { | 		instance.socket.on('streamdata', async msg => { | ||||||
| 			if (!instance.streaming[msg.id]) | 			let stream = instance.streaming[msg.id]; | ||||||
|  | 			if (!stream) | ||||||
| 				return; | 				return; | ||||||
| 			let source = audioctx.createBufferSource(); | 			let source = stream.audioctx.createBufferSource(); | ||||||
| 			source.buffer = await audioctx.decodeAudioData(msg.audio); | 			source.buffer = await stream.audioctx.decodeAudioData(msg.audio); | ||||||
| 			source.connect(audioctx.destination); | 			source.connect(stream.gain); | ||||||
| 			source.start(/*audioStartTime*/); | 			source.start(/*audioStartTime*/); | ||||||
| 		}); | 		}); | ||||||
|  | 
 | ||||||
|  | 		instance.socket.on('listeners', msg => { | ||||||
|  | 			document.querySelector( | ||||||
|  | 				`#instance${instance.id} > #threads > #threadlist > #thread${msg.thread}`) | ||||||
|  | 				.thread.listeners = msg.count; | ||||||
|  | 			if (msg.thread === window.currentThread?.id) | ||||||
|  | 				document.getElementById('listeners').innerText = | ||||||
|  | 					msg.count ? msg.count + ' listeners' : ''; | ||||||
|  | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (window.currentThread) | 	if (window.currentThread) | ||||||
|  |  | ||||||
|  | @ -110,7 +110,7 @@ io.on('connection', (socket) => { | ||||||
| 		for (let id in vybe.streams) { | 		for (let id in vybe.streams) { | ||||||
| 			const stream = vybe.streams[id]; | 			const stream = vybe.streams[id]; | ||||||
| 			delete stream.listeners[socket.id]; | 			delete stream.listeners[socket.id]; | ||||||
| 			if (stream.socket === socket.id) | 			if (stream.socket === socket) | ||||||
| 				stream.stop(); | 				stream.stop(); | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
|  | @ -35,7 +35,7 @@ async function stream(msg, respond, socket) { | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if (msg.id) { | 	if (typeof msg.id === 'number') { | ||||||
| 		stream = vybe.streams[msg.id]; | 		stream = vybe.streams[msg.id]; | ||||||
| 		if (!stream) | 		if (!stream) | ||||||
| 			return respond({ | 			return respond({ | ||||||
|  | @ -79,7 +79,7 @@ async function stream(msg, respond, socket) { | ||||||
| 			stream, | 			stream, | ||||||
| 			userid: msg.auth_user.id, | 			userid: msg.auth_user.id, | ||||||
| 			listeners: {}, | 			listeners: {}, | ||||||
| 			socket: socket.id, | 			socket, | ||||||
| 			stop: async () => { | 			stop: async () => { | ||||||
| 				stream.stopped = true; | 				stream.stopped = true; | ||||||
| 				thread.streams.splice(thread.streams.findIndex(s => s.id === stream.id), 1); | 				thread.streams.splice(thread.streams.findIndex(s => s.id === stream.id), 1); | ||||||
|  | @ -127,15 +127,20 @@ async function play_stream(msg, respond, socket) { | ||||||
| 			message: "user doesn't have permission" | 			message: "user doesn't have permission" | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 	if (!vybe.streams[msg.id]) | 	let stream = vybe.streams[msg.id]; | ||||||
|  | 	if (!stream) | ||||||
| 		return respond({ | 		return respond({ | ||||||
| 			success: false, | 			success: false, | ||||||
| 			message: 'stream not found' | 			message: 'stream not found' | ||||||
| 		}); | 		}); | ||||||
| 	if (msg.playing) | 	if (msg.playing) | ||||||
| 		vybe.streams[msg.id].listeners[socket.id] = socket; | 		stream.listeners[socket.id] = socket; | ||||||
| 	else | 	else | ||||||
| 		delete vybe.streams[msg.id].listeners[socket.id]; | 		delete stream.listeners[socket.id]; | ||||||
|  | 	stream.socket.emit('listeners', { | ||||||
|  | 		thread: stream.stream.thread, | ||||||
|  | 		count: Object.keys(stream.listeners).length | ||||||
|  | 	}); | ||||||
| 	respond({ | 	respond({ | ||||||
| 		success: true | 		success: true | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue