diff options
author | MetaMuffin <metamuffin@yandex.com> | 2021-08-06 13:20:35 +0200 |
---|---|---|
committer | MetaMuffin <metamuffin@yandex.com> | 2021-08-06 13:20:35 +0200 |
commit | a8f89036b5788a3f6ddb053824650b913a0c98a3 (patch) | |
tree | 7bdd48b3d95b7e33fc59da046bedfe68129ad559 /source | |
parent | 2387d8910d80b86a95e8c3242bdb6809dddda1aa (diff) | |
download | keks-meet-a8f89036b5788a3f6ddb053824650b913a0c98a3.tar keks-meet-a8f89036b5788a3f6ddb053824650b913a0c98a3.tar.bz2 keks-meet-a8f89036b5788a3f6ddb053824650b913a0c98a3.tar.zst |
idk
Diffstat (limited to 'source')
-rw-r--r-- | source/client/helper.ts | 14 | ||||
-rw-r--r-- | source/client/index.ts | 36 | ||||
-rw-r--r-- | source/client/local_user.ts | 51 | ||||
-rw-r--r-- | source/client/room.ts | 6 | ||||
-rw-r--r-- | source/client/user.ts | 79 | ||||
-rw-r--r-- | source/server/index.ts | 1 |
6 files changed, 157 insertions, 30 deletions
diff --git a/source/client/helper.ts b/source/client/helper.ts new file mode 100644 index 0000000..19c4f46 --- /dev/null +++ b/source/client/helper.ts @@ -0,0 +1,14 @@ +export function get_query_params(): { [key: string]: string } { + const q: { [key: string]: string } = {} + for (const kv of window.location.search.substr(1).split("&")) { + const [key, value] = kv.split("=") + q[decodeURIComponent(key)] = decodeURIComponent(value) + } + return q +} + +export function hex_id(len: number = 8): string { + if (len > 8) return hex_id() + hex_id(len - 8) + return Math.floor(Math.random() * 16 ** len).toString(16).padStart(len, "0") +} + diff --git a/source/client/index.ts b/source/client/index.ts index ce3533f..313cd86 100644 --- a/source/client/index.ts +++ b/source/client/index.ts @@ -1,3 +1,4 @@ +import { get_query_params } from "./helper" import { log } from "./logger" import { Room } from "./room" @@ -11,17 +12,46 @@ export interface User { stream: MediaStream, } -export const users: Map<string, User> = new Map() +export var parameters = get_query_params() window.onload = () => main() -export async function main() { +export async function main() { if (window.location.pathname.startsWith("/room/")) { const room_name = window.location.pathname.substr("/room/".length) let room = new Room(room_name) document.body.append(room.el) } else { - //TODO show ui for joining rooms + document.body.append(create_start_screen()) } } + +function create_start_screen() { + const el = document.createElement("div") + const header = document.createElement("h2") + header.textContent = "keks meet" + const para = document.createElement("p") + para.textContent = "Hier kann man dann irgendwann mal sinnvollen text hinschreiben..." + + // const room_input_label = document.createElement("label") + // room_input_label.textContent = "Room ID: " + // room_input_label.htmlFor = "room-id-input" + + const room_input = document.createElement("input") + room_input.type = "text" + room_input.id = "room-id-input" + room_input.placeholder = "room id " + + const submit = document.createElement("input") + submit.type = "button" + submit.addEventListener("click", () => { + if (room_input.value.length == 0) room_input.value = Math.floor(Math.random() * 10000).toString(16).padStart(5, "0") + window.location.pathname = `/room/${encodeURIComponent(room_input.value)}` + }) + submit.value = "Join room" + + el.classList.add("start-box") + el.append(header, para, room_input, document.createElement("br"), submit) + return el +} diff --git a/source/client/local_user.ts b/source/client/local_user.ts index 70a8618..3fe6216 100644 --- a/source/client/local_user.ts +++ b/source/client/local_user.ts @@ -1,3 +1,4 @@ +import { log } from "./logger"; import { RemoteUser } from "./remote_user"; import { Room } from "./room"; import { User } from "./user"; @@ -8,23 +9,37 @@ export class LocalUser extends User { private audio_track?: MediaStreamTrack private video_track?: MediaStreamTrack + controls: { audio?: HTMLElement, video?: HTMLElement } = {} + constructor(room: Room, name: string) { super(room, name) + this.el.classList.add("local") this.create_controls() - //@ts-ignore - window.ea = () => this.enable_audio() - //@ts-ignore - window.da = () => this.disable_audio() - //@ts-ignore - window.ev = () => this.enable_video() - //@ts-ignore - window.dv = () => this.disable_video() } create_controls() { - setTimeout(() => { - this.enable_video() - }, 3000) + const audio_toggle = document.createElement("input") + const video_toggle = document.createElement("input") + audio_toggle.type = video_toggle.type = "button" + audio_toggle.value = "Audio" + video_toggle.value = "Video" + let audio = false, video = false + + audio_toggle.addEventListener("click", () => { + audio = !audio + if (audio) this.enable_audio() + else this.disable_audio() + }) + video_toggle.addEventListener("click", () => { + video = !video + if (video) this.enable_video() + else this.disable_video() + }) + + const el = document.createElement("div") + el.classList.add("local-controls") + el.append(audio_toggle, video_toggle) + document.body.append(el) } async add_initial_to_remote(ru: RemoteUser) { @@ -34,25 +49,31 @@ export class LocalUser extends User { async enable_video() { if (this.video_track) return + log("media", "requesting user media (video)") const user_media = await window.navigator.mediaDevices.getUserMedia({ video: true }) - console.log(user_media.getVideoTracks()); const t = this.video_track = user_media.getVideoTracks()[0] this.room.remote_users.forEach(u => u.peer.addTrack(t)) + this.stream.addTrack(t) + this.update_view() } async enable_audio() { if (this.audio_track) return + log("media", "requesting user media (audio)") const user_media = await window.navigator.mediaDevices.getUserMedia({ audio: true }) const t = this.audio_track = user_media.getAudioTracks()[0] this.room.remote_users.forEach(u => u.peer.addTrack(t)) + this.stream.addTrack(t) + this.update_view() } async disable_video() { if (!this.video_track) return this.room.remote_users.forEach(u => { u.peer.getSenders().forEach(s => { - console.log(u, s, this.video_track); if (s.track == this.video_track) u.peer.removeTrack(s) }) }) + this.stream.removeTrack(this.video_track) + this.update_view() this.video_track = undefined } async disable_audio() { @@ -62,9 +83,9 @@ export class LocalUser extends User { if (s.track == this.audio_track) u.peer.removeTrack(s) }) }) + this.stream.removeTrack(this.audio_track) + this.update_view() this.audio_track = undefined } - - }
\ No newline at end of file diff --git a/source/client/room.ts b/source/client/room.ts index 9d315ba..fd48eff 100644 --- a/source/client/room.ts +++ b/source/client/room.ts @@ -3,6 +3,8 @@ import { CSPacket, SCPacket } from "./types"; import { RemoteUser } from "./remote_user"; import { User } from "./user"; import { LocalUser } from "./local_user"; +import { parameters } from "."; +import { hex_id } from "./helper"; export class Room { @@ -16,14 +18,14 @@ export class Room { constructor(name: string) { this.name = name this.el = document.createElement("div") - + this.el.classList.add("room") this.websocket = new WebSocket(`ws://${window.location.host}/room/${encodeURIComponent(name)}`) this.websocket.onclose = () => this.websocket_close() this.websocket.onopen = () => this.websocket_open() this.websocket.onmessage = (ev) => { this.websocket_message(JSON.parse(ev.data)) } - this.local_user = new LocalUser(this, Math.random().toString()) + this.local_user = new LocalUser(this, parameters.username ?? `guest-${hex_id()}`) } websocket_send(data: CSPacket) { diff --git a/source/client/user.ts b/source/client/user.ts index 7d6991a..579886f 100644 --- a/source/client/user.ts +++ b/source/client/user.ts @@ -7,7 +7,9 @@ export abstract class User { room: Room el: HTMLElement - view_el?: HTMLElement + media_el?: HTMLElement + + display?: { audio_status_el: HTMLElement, video_status_el: HTMLElement } local: boolean = false @@ -17,7 +19,9 @@ export abstract class User { this.name = name this.room = room this.el = document.createElement("div") + this.el.classList.add("user") this.room.el.append(this.el) + this.setup_view() this.update_view() } @@ -41,19 +45,74 @@ export abstract class User { } } + setup_view() { + const info_el = document.createElement("div") + info_el.classList.add("info") + const name_el = document.createElement("span") + name_el.textContent = this.name + name_el.classList.add("name") + const audio_status_el = document.createElement("span") + const video_status_el = document.createElement("span") + video_status_el.classList.add("status", "video-status") + audio_status_el.classList.add("status", "audio-status") + audio_status_el.textContent = "A" + video_status_el.textContent = "V" + info_el.append(audio_status_el, video_status_el, name_el) + this.display = { video_status_el, audio_status_el } + this.el.append(info_el) + } + update_view() { - if (this.view_el) this.el.removeChild(this.view_el) - this.view_el = this.create_view() - this.el.appendChild(this.view_el) + if (this.stream.getAudioTracks().length > 0) + this.display?.audio_status_el.classList.add("enabled") + else this.display?.audio_status_el.classList.remove("enabled") + + if (this.stream.getVideoTracks().length > 0) + this.display?.video_status_el.classList.add("enabled") + else this.display?.video_status_el.classList.remove("enabled") + + if (this.media_el) this.el.removeChild(this.media_el) + this.media_el = this.create_media_view() + this.el.appendChild(this.media_el) } - create_view() { - const el = document.createElement("video") - el.autoplay = true - el.toggleAttribute("playsinline") - el.srcObject = this.stream - console.log(el); + create_media_view() { + const has_video = this.stream.getVideoTracks().length > 0 + const has_audio = this.stream.getAudioTracks().length > 0 + const media_el = has_video ? document.createElement("video") : document.createElement("audio") + media_el.classList.add("media") + media_el.autoplay = true + if (has_video) media_el.toggleAttribute("playsinline") + media_el.srcObject = this.stream + if (has_video) media_el.addEventListener("click", () => { + media_el.classList.remove("maximized") + }) + + const controls_el = document.createElement("div") + controls_el.classList.add("media-controls") + if (has_video) { + const pip_el = document.createElement("input") + pip_el.type = "button" + pip_el.addEventListener("click", () => { + //@ts-ignore + media_el.requestPictureInPicture() + }) + pip_el.value = "Picture-in-Picture" + const max_el = document.createElement("input") + max_el.type = "button" + max_el.addEventListener("click", () => { + media_el.classList.add("maximized") + }) + max_el.value = "Maximize" + controls_el.append(max_el, pip_el) + } + if (has_audio) { + // TODO volume controls + } + const el = document.createElement("div") + el.classList.add("media-container") + el.append(media_el, controls_el) return el } }
\ No newline at end of file diff --git a/source/server/index.ts b/source/server/index.ts index ef70492..470e289 100644 --- a/source/server/index.ts +++ b/source/server/index.ts @@ -51,6 +51,7 @@ async function main() { let user_name = "" const init = (n: string) => { + if (room.get(n)) return ws.close(1, "username already taken") initialized = true user_name = n rooms.set(req.params.id, room) |