streams, message time, space zoom+pan
							parent
							
								
									bb31b56929
								
							
						
					
					
						commit
						779aaece08
					
				|  | @ -162,6 +162,7 @@ | |||
| 				} | ||||
| 				> .title { | ||||
| 					width: 100%; | ||||
| 					padding-inline: 4px; | ||||
| 				} | ||||
| 			} | ||||
| 			.expander { | ||||
|  | @ -301,6 +302,12 @@ | |||
| 			.message { | ||||
| 				margin-bottom: 5px; | ||||
| 				overflow-wrap: anywhere; | ||||
| 				display: flex; | ||||
| 			} | ||||
| 			.time { | ||||
| 				color: #999; | ||||
| 				font-size: small; | ||||
| 				vertical-align: top; | ||||
| 			} | ||||
| 			#loadmore { | ||||
| 				margin-bottom: 10px; | ||||
|  | @ -321,9 +328,12 @@ | |||
| 			} | ||||
| 			#space { | ||||
| 				margin: -2px; /* offset column margin */ | ||||
| 				position: relative; | ||||
| 				overflow: auto; | ||||
| 			} | ||||
| 			#spacediv { | ||||
| 				position: relative; | ||||
| 				transform-origin: 0 0; | ||||
| 			} | ||||
| 			.span { | ||||
| 				position: absolute; | ||||
| 				white-space: nowrap; | ||||
|  |  | |||
|  | @ -1,45 +1,56 @@ | |||
| import { render, html } from '/uhtml.js'; | ||||
| 
 | ||||
| let msg; | ||||
| 
 | ||||
| function sendMessage(event) { | ||||
| 	event.preventDefault(); | ||||
| 	if (!msg.value) | ||||
| 		return; | ||||
| 	window.currentInstance.emit('send_message', { | ||||
| 		message: msg.value, | ||||
| 		thread: window.currentThread.id | ||||
| 	}); | ||||
| 	msg.value = ''; | ||||
| } | ||||
| 
 | ||||
| let earliestMessage; | ||||
| 
 | ||||
| function loadMessages(callback) { | ||||
| 	let instance = window.currentInstance; | ||||
| 
 | ||||
|    if (!msg) { | ||||
|       render(document.getElementById('message'), html` | ||||
|          <button id='loadmore' class='hidden' onclick=${loadMessages}> | ||||
|          load more messages | ||||
|          </button> | ||||
|          <div id='messages'></div> | ||||
|          <form id='msginput' onsubmit=${sendMessage}> | ||||
|             <input id='msg' placeholder='write a message...' /> | ||||
|             <button type='submit' id='sendmsg'>send</button> | ||||
|          </form> | ||||
|       `);
 | ||||
|       msg = document.getElementById('msg'); | ||||
|    } | ||||
|    const messages = document.getElementById('messages'); | ||||
|    if (!this) { // called from chooseThread, initializing thread
 | ||||
|    	messages.innerHTML = ''; | ||||
|       earliestMessage = null; | ||||
| 	if (!msg) { | ||||
| 		render(document.getElementById('message'), html` | ||||
| 			<button id='loadmore' class='hidden' onclick=${loadMessages}> | ||||
| 			load more messages | ||||
| 			</button> | ||||
| 			<div id='messages'></div> | ||||
| 			<form id='msginput' onsubmit=${function(event) { | ||||
| 				event.preventDefault(); | ||||
| 				if (!msg.value) | ||||
| 					return; | ||||
| 				window.currentInstance.emit('send_message', { | ||||
| 					message: msg.value, | ||||
| 					thread: window.currentThread.id | ||||
| 				}); | ||||
| 				msg.value = ''; | ||||
| 			}}> | ||||
| 				<input id='msg' placeholder='write a message...' /> | ||||
| 				<button type='submit' id='sendmsg'>send</button> | ||||
| 			</form> | ||||
| 		`);
 | ||||
| 		msg = document.getElementById('msg'); | ||||
| 	} | ||||
| 	const messages = document.getElementById('messages'); | ||||
| 	if (!this) { // called from chooseThread, initializing thread
 | ||||
| 		messages.innerHTML = ''; | ||||
| 		earliestMessage = null; | ||||
| 		if (window.currentThread.permissions.post) | ||||
| 			document.getElementById('msginput').classList.remove('hidden'); | ||||
| 		else | ||||
| 			document.getElementById('msginput').classList.add('hidden'); | ||||
|    } | ||||
| 	} | ||||
| 
 | ||||
| 	function addMessage(message, user) { | ||||
| 		let now = new Date(), date = new Date(message.created); | ||||
| 		let div = html.node`<div class='message'><span class='time'>${ | ||||
| 			now.getDate() === date.getDate() && date.getTime() > now.getTime() - 24 * 60 * 60000 | ||||
| 			? date.toLocaleTimeString() : date.toLocaleString() | ||||
| 		}</span></div>`; | ||||
| 		let content = html.node`<span class='content'>: ${message.content}</span>`; | ||||
| 		content.prepend(user ? window.makeUser(user, user.id.split?.('@')[1] || instance.url) | ||||
| 			: html.node`<span>${message.user.id}</span>`); | ||||
| 		div.prepend(content); | ||||
| 		messages.append(div); | ||||
| 	} | ||||
| 
 | ||||
| 	instance.emit('get_history', { | ||||
| 		before: earliestMessage, | ||||
| 		thread: window.currentThread.id | ||||
|  | @ -73,10 +84,7 @@ function loadMessages(callback) { | |||
| 						user.permissions = message.user.permissions; | ||||
| 					} | ||||
| 				} | ||||
| 				let div = html.node`<div class='message'>: ${message.content}</div>`; | ||||
| 				div.prepend(user ? window.makeUser(user, user.id.split?.('@')[1] || instance.url) | ||||
| 					: html.node`<span>${message.user.id}</span>`); | ||||
| 				messages.prepend(div); | ||||
| 				addMessage(message, user); | ||||
| 			} | ||||
| 		} | ||||
| 		if (msg.more) | ||||
|  | @ -92,10 +100,7 @@ function loadMessages(callback) { | |||
| 				return; | ||||
| 			const messages = document.getElementById('messages'); | ||||
| 			let scroll = messages.scrollTop + 10 >= messages.scrollHeight - messages.clientHeight; | ||||
| 			let div = html.node`<div class='message'>: ${message.content}</div>`; | ||||
| 			div.prepend(window.makeUser(message.user, | ||||
| 				message.user.id.split?.('@')[1] || instance.url)); | ||||
| 			messages.append(div); | ||||
| 			addMessage(message, message.user); | ||||
| 			if (scroll) | ||||
| 				messages.scroll(0, messages.scrollHeight - messages.clientHeight); | ||||
| 			if (!earliestMessage) | ||||
|  |  | |||
|  | @ -1,23 +1,30 @@ | |||
| let space; | ||||
| 
 | ||||
| let scale = 1; // todo: make zooming work
 | ||||
| let spaceContainer, space; | ||||
| 
 | ||||
| let editing; | ||||
| let dragging; | ||||
| let moved; | ||||
| let movedFrom; | ||||
| let offset; | ||||
| let clicked; | ||||
| 
 | ||||
| document.onmousemove = event => { | ||||
| document.addEventListener('mouseup', function(event) { | ||||
| 	clicked = false; | ||||
| }); | ||||
| 
 | ||||
| document.addEventListener('mousemove', function(event) { | ||||
| 	moved = true; | ||||
| 	if (!dragging) | ||||
| 		return; | ||||
| 	let left = (event.clientX - space.offsetLeft) * scale - offset.x; | ||||
| 	let top = (event.clientY - space.offsetTop) * scale - offset.y; | ||||
| 	dragging.style.left = `${left < 0 ? 0 : left}px`; | ||||
| 	dragging.style.top = `${top < 0 ? 0 : top}px`; | ||||
| 	save(dragging); | ||||
| }; | ||||
| 	if (dragging) { | ||||
| 		let left = event.clientX - spaceContainer.offsetLeft - offset.x; | ||||
| 		let top = event.clientY - spaceContainer.offsetTop - offset.y; | ||||
| 		dragging.style.left = `${left < 0 ? 0 : left}px`; | ||||
| 		dragging.style.top = `${top < 0 ? 0 : top}px`; | ||||
| 		save(dragging); | ||||
| 	} | ||||
| 	else if (clicked) { | ||||
| 		spaceContainer.scrollLeft -= event.movementX; | ||||
| 		spaceContainer.scrollTop -= event.movementY; | ||||
| 	} | ||||
| }); | ||||
| 
 | ||||
| let saving; | ||||
| let queue; | ||||
|  | @ -79,24 +86,28 @@ function add(s) { | |||
| 	}; | ||||
| 	span.onwheel = function(event) { | ||||
| 		event.preventDefault(); | ||||
| 		if (event.deltaY < 0 && this.scale >= 200) | ||||
| 			return; | ||||
| 		event.stopPropagation(); | ||||
| 		this.scale *= 1 - event.deltaY * .001; | ||||
| 		if (this.scale > 100) | ||||
| 			this.scale = 100; | ||||
| 		this.style.transform = `translate(-50%, -50%) scale(${this.scale})`; | ||||
| 		save(this); | ||||
| 	}; | ||||
| 	span.onmousedown = function(event) { | ||||
| 		if (dragging || editing === this) | ||||
| 			return; | ||||
| 		dragging = this; | ||||
| 		event.preventDefault(); | ||||
| 		if (event.button !== 0) | ||||
| 			return; | ||||
| 		dragging = this; | ||||
| 		offset = { | ||||
| 			x: event.clientX - (space.offsetLeft + this.offsetLeft), | ||||
| 			y: event.clientY - (space.offsetTop + this.offsetTop) | ||||
| 			x: event.clientX - (spaceContainer.offsetLeft + this.offsetLeft), | ||||
| 			y: event.clientY - (spaceContainer.offsetTop + this.offsetTop) | ||||
| 		}; | ||||
| 	}; | ||||
| 	span.onmouseup = function(event) { | ||||
| 		event.stopPropagation(); | ||||
| 		if (event.button !== 0) | ||||
| 			return; | ||||
| 		dragging = null; | ||||
| 		if (moved) | ||||
| 			return; | ||||
|  | @ -109,14 +120,36 @@ function add(s) { | |||
| 
 | ||||
| export default function loadSpace(callback) { | ||||
| 	let instance = window.currentInstance; | ||||
| 	 | ||||
| 	if (!instance.spaceid) { | ||||
| 		space = document.getElementById('space'); | ||||
| 		space.onmousedown = event => { | ||||
| 
 | ||||
| 	let scale = 1; | ||||
| 
 | ||||
| 	if (!instance.spaced) { | ||||
| 		spaceContainer = document.getElementById('space'); | ||||
| 		space = document.getElementById('spacediv'); | ||||
| 
 | ||||
| 		spaceContainer.onwheel = function(event) { | ||||
| 			if (event.getModifierState('Shift')) | ||||
| 				return; | ||||
| 			event.preventDefault(); | ||||
| 			scale *= 1 - event.deltaY * .001; | ||||
| 			if (scale < .5) | ||||
| 				scale = .5; | ||||
| 			space.style.transform = `scale(${scale})`; | ||||
| 			spaceContainer.scrollLeft += event.offsetX * (scale - scale / (1 - event.deltaY * .001)); | ||||
| 			spaceContainer.scrollTop += event.offsetY * (scale - scale / (1 - event.deltaY * .001)); | ||||
| 			//spaceContainer.scrollLeft += spaceContainer.clientWidth * (1 - spaceContainer.clientWidth / spaceContainer.scrollWidth + event.offsetX / spaceContainer.clientWidth) * (scale - scale / (1 - event.deltaY * .001));
 | ||||
| 			//spaceContainer.scrollTop += spaceContainer.clientHeight * (1 - spaceContainer.clientHeight / spaceContainer.scrollHeight + event.offsetY / spaceContainer.clientHeight) * (scale - scale / (1 - event.deltaY * .001));
 | ||||
| 		}; | ||||
| 		spaceContainer.onmousedown = function(event) { | ||||
| 			if (event.button !== 0) | ||||
| 				return; | ||||
| 			clicked = true; | ||||
| 			moved = false; | ||||
| 			movedFrom = { x: event.offsetX, y: event.offsetY }; | ||||
| 		}; | ||||
| 		space.onmouseup = event => { | ||||
| 		spaceContainer.onmouseup = function(event) { | ||||
| 			if (event.button !== 0) | ||||
| 				return; | ||||
| 			if (dragging) { | ||||
| 				dragging.onmouseup(event); | ||||
| 				return; | ||||
|  | @ -130,8 +163,8 @@ export default function loadSpace(callback) { | |||
| 				+ (event.offsetY - movedFrom.y) * (event.offsetY - movedFrom.y) > 100) | ||||
| 				return; | ||||
| 			editing = add({ | ||||
| 				x: event.offsetX + space.scrollLeft, | ||||
| 				y: event.offsetY + space.scrollTop, | ||||
| 				x: (event.offsetX + spaceContainer.scrollLeft) / scale, | ||||
| 				y: (event.offsetY + spaceContainer.scrollTop) / scale, | ||||
| 				scale: 1, | ||||
| 				content: '' | ||||
| 			}); | ||||
|  | @ -139,7 +172,7 @@ export default function loadSpace(callback) { | |||
| 		}; | ||||
| 
 | ||||
| 		instance.socket.on('span', msg => { | ||||
| 			if (msg.thread !== instance.spaceid || window.currentInstance !== instance) | ||||
| 			if (msg.thread !== window.currentSpace.id || window.currentInstance !== instance) | ||||
| 				return; | ||||
| 			let span = document.getElementById('span' + msg.id); | ||||
| 			if (span) { | ||||
|  | @ -152,13 +185,15 @@ export default function loadSpace(callback) { | |||
| 			else | ||||
| 				add(msg); | ||||
| 		}); | ||||
| 		instance.spaced = true; | ||||
| 	} | ||||
| 	else if (instance.spaceid === window.currentThread.id) | ||||
| 	else if (window.currentSpace === window.currentThread) | ||||
| 		return; | ||||
| 	instance.spaceid = window.currentThread.id; | ||||
| 	window.currentSpace = window.currentThread; | ||||
| 	space.innerHTML = ''; | ||||
| 	space.style.transform = ''; | ||||
| 	instance.emit('get_space', { | ||||
| 		thread: window.currentThread.id | ||||
| 		thread: window.currentSpace.id | ||||
| 	}, msg => { | ||||
| 		if (!msg.success) { | ||||
| 			console.log('get space failed: ' + msg.message); | ||||
|  |  | |||
							
								
								
									
										111
									
								
								client/stream.js
								
								
								
								
							
							
						
						
									
										111
									
								
								client/stream.js
								
								
								
								
							|  | @ -2,8 +2,7 @@ import { render, html } from '/uhtml.js'; | |||
| 
 | ||||
| let mediaStream; | ||||
| 
 | ||||
| async function stream() { | ||||
| 	let thread = window.currentThread; | ||||
| async function stream(thread) { | ||||
| 	if (thread.handle) { | ||||
| 		clearInterval(thread.handle); | ||||
| 		delete thread.handle; | ||||
|  | @ -35,40 +34,40 @@ async function stream() { | |||
| 	thread.instance.emit('stream', { | ||||
| 		thread: thread.id, | ||||
| 		name: document.getElementById('streamname').value | ||||
| 	}, msg => { | ||||
| 	}, async msg => { | ||||
| 		if (!msg.success) { | ||||
| 			console.log('stream failed:', msg.message); | ||||
| 			return; | ||||
| 		} | ||||
| 		thread.streamid = msg.id; | ||||
| 		document.getElementById('streaming').innerText = 'stop streaming'; | ||||
| 		function record() { | ||||
| 			let r = thread.recorder = new MediaRecorder(mediaStream); | ||||
| 			let chunks = []; | ||||
| 			r.ondataavailable = event => { | ||||
| 				if (!event.data.size) | ||||
| 		thread.recorder = new MediaRecorder(mediaStream, { | ||||
| 			mimeType: 'audio/webm;codecs=opus' | ||||
| 		}); | ||||
| 		thread.recorder.start(); | ||||
| 		thread.recorder.ondataavailable = async event => { | ||||
| 			if (!event.data.size || !thread.handle) | ||||
| 				return; | ||||
| 			thread.instance.emit('streamdata', { | ||||
| 				id: thread.streamid, | ||||
| 				audio: await event.data.arrayBuffer() | ||||
| 			}, msg => { | ||||
| 				if (msg.success) | ||||
| 					return; | ||||
| 				chunks.push(event.data); | ||||
| 			}; | ||||
| 			r.onstop = async () => { | ||||
| 				if (!chunks.length || !thread.handle) | ||||
| 				console.log('streamdata failed:', msg.message); | ||||
| 				if (msg.message === 'stream not found' && thread.handle) | ||||
| 					stream(thread); | ||||
| 			}); | ||||
| 		}; | ||||
| 		// first 200ms chunk will be used as stream header
 | ||||
| 		thread.handle = setTimeout(() => { | ||||
| 			thread.recorder.requestData(); | ||||
| 			thread.handle = setInterval(() => { | ||||
| 				if (!thread.handle) | ||||
| 					return; | ||||
| 				//console.log(`${Date.now()} ${chunks.length}`);
 | ||||
| 				thread.instance.emit('streamdata', { | ||||
| 					id: thread.streamid, | ||||
| 					audio: await (new Blob(chunks, { type: chunks[0].type })).arrayBuffer() | ||||
| 				}, msg => { | ||||
| 					if (!msg.success) | ||||
| 						console.log('streamdata failed:', msg.message); | ||||
| 				}); | ||||
| 			}; | ||||
| 			r.onstart = () => { | ||||
| 				setTimeout(() => r.state === 'recording' && r.stop(), 500); | ||||
| 			}; | ||||
| 			r.start(); | ||||
| 		} | ||||
| 		record(); | ||||
| 		thread.handle = setInterval(record, 500); | ||||
| 				thread.recorder.requestData(); | ||||
| 			}, 500); | ||||
| 		}, 200); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
|  | @ -79,24 +78,24 @@ function loadStreams() { | |||
| 	div.innerHTML = ''; | ||||
| 	if (window.currentThread?.permissions.post) { | ||||
| 		render(div, html.node` | ||||
| 			<button id='streaming' onclick=${stream}> | ||||
| 				${window.currentThread.handle ? 'stop' : 'start'} streaming | ||||
| 			</button> | ||||
| 			<button id='streaming' onclick=${function(event){ | ||||
| 				stream(currentThread); | ||||
| 			}}>${currentThread.handle ? 'stop' : 'start'} streaming</button> | ||||
| 			<span>stream name:</span> | ||||
| 			<input id='streamname' oninput=${function(event) { | ||||
| 				if (window.currentThread.handle) | ||||
| 				if (currentThread.handle) | ||||
| 					instance.emit('stream', { | ||||
| 						id: window.currentThread.streamid, | ||||
| 						thread: window.currentThread.id, | ||||
| 						id: currentThread.streamid, | ||||
| 						thread: currentThread.id, | ||||
| 						name: this.value | ||||
| 					}); | ||||
| 				window.currentThread.streamname = this.value; | ||||
| 				currentThread.streamname = this.value; | ||||
| 			}}> | ||||
| 			<p id='listeners'>${window.currentThread.listeners ? | ||||
| 				window.currentThread.listeners + ' listeners' : ''} | ||||
| 			<p id='listeners'>${currentThread.listeners ? | ||||
| 				currentThread.listeners + ' listeners' : ''} | ||||
| 			</p>`); | ||||
| 		if (window.currentThread.streamname) | ||||
| 			document.getElementById('streamname').value = window.currentThread.streamname; | ||||
| 		if (currentThread.streamname) | ||||
| 			document.getElementById('streamname').value = currentThread.streamname; | ||||
| 	} | ||||
| 	div.insertAdjacentHTML('beforeend', ` | ||||
| 		<p>streams:</p> | ||||
|  | @ -107,16 +106,23 @@ function loadStreams() { | |||
| 			<p> | ||||
| 				<button id='play' onclick=${function(event) { | ||||
| 					if (stream.playing) { | ||||
| 						stream.audioctx.suspend(); | ||||
| 						stream.audio.pause(); | ||||
| 						delete instance.streaming[stream.id]; | ||||
| 						stream.playing = false; | ||||
| 						this.innerText = '▶'; | ||||
| 					} | ||||
| 					else { | ||||
| 						stream.audioctx = new AudioContext(); | ||||
| 						stream.gain = stream.audioctx.createGain(); | ||||
| 						stream.gain.connect(stream.audioctx.destination); | ||||
| 						stream.gain.gain.value = p.children['volume'].value / 100; | ||||
| 						stream.audio = new Audio(); | ||||
| 						// play 200ms stream header silently
 | ||||
| 						stream.audio.volume = 0; | ||||
| 						stream.audio.onplaying = () => setTimeout(() => { | ||||
| 							stream.audio.volume = p.children['volume'].value / 100; | ||||
| 							p.children['volume'].oninput = function(event) { | ||||
| 								stream.audio.volume = this.value / 100; | ||||
| 							}; | ||||
| 						}, 200); | ||||
| 						stream.audio.src = '/stream/' + stream.id; | ||||
| 						stream.audio.play(); | ||||
| 						instance.streaming[stream.id] = stream; | ||||
| 						stream.playing = true; | ||||
| 						this.innerText = '⏹'; | ||||
|  | @ -127,13 +133,10 @@ function loadStreams() { | |||
| 						playing: stream.playing | ||||
| 					}, msg => { | ||||
| 						if (!msg.success) | ||||
| 							console.log('play stream failed: ', msg.message); | ||||
| 							console.log('play_stream failed: ', msg.message); | ||||
| 					}); | ||||
| 				}}>${instance.streaming[stream.id] ? '⏹' : '▶'}</button> | ||||
| 				<input id='volume' type='range' oninput=${function(event) { | ||||
| 					if (stream.gain) | ||||
| 						stream.gain.gain.value = this.value / 100; | ||||
| 				}}> | ||||
| 				<input id='volume' type='range'> | ||||
| 				<span id='name'>${stream.name ? ` - ${stream.name}` : ''}</span> | ||||
| 			</p>`; | ||||
| 		p.insertBefore(window.makeUser(stream.user, | ||||
|  | @ -171,16 +174,6 @@ function loadStreams() { | |||
| 			} | ||||
| 		}); | ||||
| 
 | ||||
| 		instance.socket.on('streamdata', async msg => { | ||||
| 			let stream = instance.streaming[msg.id]; | ||||
| 			if (!stream) | ||||
| 				return; | ||||
| 			let source = stream.audioctx.createBufferSource(); | ||||
| 			source.buffer = await stream.audioctx.decodeAudioData(msg.audio); | ||||
| 			source.connect(stream.gain); | ||||
| 			source.start(/*audioStartTime*/); | ||||
| 		}); | ||||
| 
 | ||||
| 		instance.socket.on('listeners', msg => { | ||||
| 			document.querySelector( | ||||
| 				`#instance${instance.id} > #threads > #threadlist > #thread${msg.thread}`) | ||||
|  |  | |||
|  | @ -419,7 +419,9 @@ async function loadThreads(instancediv, select) { | |||
| 					}>members</button> | ||||
| 				</div> | ||||
| 				<div id='message' class='tabcontent'></div> | ||||
| 				<div id='space' class='tabcontent hidden'></div> | ||||
| 				<div id='space' class='tabcontent hidden'> | ||||
| 					<div id='spacediv'></div> | ||||
| 				</div> | ||||
| 				<div id='call' class='tabcontent hidden'></div> | ||||
| 			</div> | ||||
| 			<hr class='separator' color='#505050'> | ||||
|  |  | |||
							
								
								
									
										15
									
								
								server.js
								
								
								
								
							
							
						
						
									
										15
									
								
								server.js
								
								
								
								
							|  | @ -4,6 +4,7 @@ const http = require('http'); | |||
| const { Server } = require('socket.io'); | ||||
| const ioclient = require('socket.io-client'); | ||||
| const compression = require('compression'); | ||||
| const { PassThrough } = require('stream'); | ||||
| 
 | ||||
| const events = {}; | ||||
| for (let file of fs.readdirSync('./src/events')) { | ||||
|  | @ -89,6 +90,20 @@ const io = new Server(server, { | |||
| 
 | ||||
| app.use(express.static('client')); | ||||
| 
 | ||||
| app.get('/stream/:id', (req, res) => { | ||||
| 	let stream = vybe.streams[req.params.id]; | ||||
| 	if (!stream) { | ||||
| 		res.sendStatus(404); | ||||
| 		return; | ||||
| 	} | ||||
| 	res.status(200); | ||||
| 	let passthrough = new PassThrough(); | ||||
| 	passthrough.pipe(res); | ||||
| 	stream.streams.add(passthrough); | ||||
| 	res.set('Content-Type', 'audio/webm'); | ||||
| 	req.on('close', () => stream.streams.delete(passthrough)); | ||||
| }); | ||||
| 
 | ||||
| io.on('connection', (socket) => { | ||||
| 	for (let event in events) { | ||||
| 		socket.on(event, (msg, callback) => { | ||||
|  |  | |||
|  | @ -46,7 +46,8 @@ async function send_message(msg, respond) { | |||
| 						permissions: userperms | ||||
| 					}, | ||||
| 					content: msg.message, | ||||
| 					thread: msg.thread | ||||
| 					thread: msg.thread, | ||||
| 					created: Date.now() | ||||
| 				}); | ||||
| 			} | ||||
| 		} | ||||
|  | @ -77,12 +78,12 @@ async function get_history(msg, respond) { | |||
| 		}); | ||||
| 	} | ||||
| 	const messages = (await db.query(` | ||||
| 		select coalesce(displayname, name) as displayname, name, user as userid, post.id, content | ||||
| 		select coalesce(displayname, name) as displayname, name, user as userid, post.id, content, post.created | ||||
| 		from post | ||||
| 		left join user on post.user = user.id | ||||
| 		where ${msg.before ? 'post.id < ? and' : ''} | ||||
| 		thread = ? | ||||
| 		order by post.created desc | ||||
| 		order by post.created asc | ||||
| 		limit 101`,
 | ||||
| 		msg.before ? [msg.before, msg.thread] : [msg.thread] | ||||
| 	)).rows; | ||||
|  | @ -103,7 +104,8 @@ async function get_history(msg, respond) { | |||
| 				displayname: message.displayname, | ||||
| 				permissions: perms[message.userid] | ||||
| 			}, | ||||
| 			content: message.content | ||||
| 			content: message.content, | ||||
| 			created: new Date(message.created + 'Z').getTime() | ||||
| 		})), | ||||
| 		more: messages.length > 100 | ||||
| 	}); | ||||
|  |  | |||
|  | @ -32,24 +32,22 @@ async function stream(msg, respond, socket) { | |||
| 		} | ||||
| 	} | ||||
| 	if (typeof msg.id === 'number') { | ||||
| 		stream = vybe.streams[msg.id]; | ||||
| 		if (!stream) | ||||
| 		let vstream = vybe.streams[msg.id]; | ||||
| 		if (!vstream) | ||||
| 			return respond({ | ||||
| 				success: false, | ||||
| 				message: 'stream not found' | ||||
| 			}); | ||||
| 		if (msg.auth_user.id !== stream.userid) | ||||
| 		if (msg.auth_user.id !== vstream.userid) | ||||
| 			return respond({ | ||||
| 				success: false, | ||||
| 				message: "stream doesn't belong to user" | ||||
| 			}); | ||||
| 		stream = vstream.stream; | ||||
| 		if (msg.stop) { | ||||
| 			await stream.stop(); | ||||
| 			return respond({ | ||||
| 				success: true | ||||
| 			}); | ||||
| 			await vstream.stop(); | ||||
| 			return respond({ success: true }); | ||||
| 		} | ||||
| 		stream = stream.stream; | ||||
| 		stream.name = msg.name; | ||||
| 		stream.user.displayname = msg.auth_user.displayname; | ||||
| 	} | ||||
|  | @ -82,7 +80,8 @@ async function stream(msg, respond, socket) { | |||
| 				thread.streams.splice(thread.streams.findIndex(s => s.id === stream.id), 1); | ||||
| 				delete vybe.streams[stream.id]; | ||||
| 				await send(); | ||||
| 			} | ||||
| 			}, | ||||
| 			streams: new Set() | ||||
| 		}; | ||||
| 	} | ||||
| 	await send(); | ||||
|  | @ -106,15 +105,11 @@ async function streamdata(msg, respond) { | |||
| 			message: "stream doesn't belong to user" | ||||
| 		}); | ||||
| 	} | ||||
| 	for (let id in stream.listeners) | ||||
| 		stream.listeners[id].emit('streamdata', { | ||||
| 			id: msg.id, | ||||
| 			audio: msg.audio, | ||||
| 			video: msg.video | ||||
| 		}); | ||||
| 	respond({ | ||||
| 		success: true | ||||
| 	}); | ||||
| 	if (stream.head) | ||||
| 		stream.streams.forEach(passthrough => passthrough.write(msg.audio)); | ||||
| 	else | ||||
| 		stream.head = msg.audio; | ||||
| 	respond({ success: true }); | ||||
| } | ||||
| 
 | ||||
| async function play_stream(msg, respond, socket) { | ||||
|  | @ -138,9 +133,7 @@ async function play_stream(msg, respond, socket) { | |||
| 		thread: stream.stream.thread, | ||||
| 		count: Object.keys(stream.listeners).length | ||||
| 	}); | ||||
| 	respond({ | ||||
| 		success: true | ||||
| 	}); | ||||
| 	respond({ success: true }); | ||||
| } | ||||
| 
 | ||||
| module.exports = { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue