aboutsummaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/client/helper.ts43
-rw-r--r--source/client/index.ts70
-rw-r--r--source/client/local_user.ts125
-rw-r--r--source/client/logger.ts50
-rw-r--r--source/client/menu.ts26
-rw-r--r--source/client/remote_user.ts74
-rw-r--r--source/client/rnnoise.ts38
-rw-r--r--source/client/room.ts72
-rw-r--r--source/client/track_handle.ts26
-rw-r--r--source/client/user.ts80
-rw-r--r--source/packets.ts21
-rw-r--r--source/server/index.ts77
-rw-r--r--source/server/room.ts58
13 files changed, 0 insertions, 760 deletions
diff --git a/source/client/helper.ts b/source/client/helper.ts
deleted file mode 100644
index 31e500a..0000000
--- a/source/client/helper.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-/// <reference lib="dom" />
-
-import { parameters } from "./index.ts"
-
-export function get_query_params(): { [key: string]: string } {
- const q: { [key: string]: string } = {}
- for (const kv of window.location.hash.substring(1).split("&")) {
- const [key, value] = kv.split("=")
- q[decodeURIComponent(key)] = decodeURIComponent(value)
- }
- return q
-}
-
-export function hex_id(len = 8): string {
- if (len > 8) return hex_id() + hex_id(len - 8)
- return Math.floor(Math.random() * 16 ** len).toString(16).padStart(len, "0")
-}
-
-export function parameter_bool(name: string, def: boolean): boolean {
- const v = parameters[name]
- if (!v) return def
- if (v == "0" || v == "false" || v == "no") return false
- if (v == "1" || v == "true" || v == "yes") return true
- alert(`parameter ${name} is invalid`)
- return def
-}
-
-export function parameter_number(name: string, def: number): number {
- const v = parameters[name]
- if (!v) return def
- const n = parseFloat(v)
- if (Number.isNaN(n)) {
- alert(`parameter ${name} is invalid`)
- return def
- }
- return n
-}
-
-export function parameter_string(name: string, def: string): string {
- const v = parameters[name]
- if (!v) return def
- return v
-}
diff --git a/source/client/index.ts b/source/client/index.ts
deleted file mode 100644
index 745dd67..0000000
--- a/source/client/index.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-/// <reference lib="dom" />
-
-import { get_query_params } from "./helper.ts"
-import { log } from "./logger.ts"
-import { create_menu } from "./menu.ts";
-import { Room } from "./room.ts"
-
-export const servers: RTCConfiguration = {
- iceServers: [{ urls: ["stun:stun1.l.google.com:19302", "stun:stun2.l.google.com:19302"] }],
- iceCandidatePoolSize: 10,
-}
-
-export interface User {
- peer: RTCPeerConnection
- stream: MediaStream,
-}
-
-export const parameters = get_query_params()
-
-window.onload = () => main()
-
-export function main() {
- document.body.querySelector("p")?.remove()
- log("*", "starting up")
- if (window.location.pathname.startsWith("/room/")) {
- const room_name = window.location.pathname.substring("/room/".length)
- const room = new Room(room_name)
- create_menu(room)
- document.body.append(room.el)
- } else {
- create_menu()
- document.body.append(create_start_screen())
- }
-}
-
-function create_start_screen() {
- const with_text_content = (a: string) => (b: string) => {
- const e = document.createElement(a)
- e.textContent = b
- return e
- }
- const p = with_text_content("p")
- const h2 = with_text_content("h2")
-
- const el = document.createElement("div")
- el.append(
- h2("keks-meet"),
- p("A web conferencing application using webrtc"),
- p("keks-meet is free software! It is licenced under the terms of the third version of the GNU Affero General Public Licence only."),
- p("To get started, just enter a unique idenfier, click 'Join', then share the URL with your partner.")
- )
-
- 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(room_input, document.createElement("br"), submit)
-
- return el
-}
diff --git a/source/client/local_user.ts b/source/client/local_user.ts
deleted file mode 100644
index ca91a19..0000000
--- a/source/client/local_user.ts
+++ /dev/null
@@ -1,125 +0,0 @@
-/// <reference lib="dom" />
-
-import { parameter_bool, parameter_number } from "./helper.ts";
-import { log } from "./logger.ts";
-import { RemoteUser } from "./remote_user.ts";
-import { get_rnnoise_node } from "./rnnoise.ts";
-import { Room } from "./room.ts";
-import { TrackHandle } from "./track_handle.ts";
-import { User } from "./user.ts";
-
-
-export class LocalUser extends User {
- mic_gain?: GainNode
- default_gain: number = parameter_number("mic_gain", 1)
-
- constructor(room: Room, name: string) {
- super(room, name)
- this.el.classList.add("local")
- this.local = true
- this.create_controls()
- this.add_initial_tracks()
- log("usermodel", `added local user: ${this.name}`)
- }
-
- async add_initial_tracks() {
- if (parameter_bool("mic_enabled", false)) this.publish_track(await this.create_mic_track())
- if (parameter_bool("camera_enabled", false)) this.publish_track(await this.create_camera_track())
- if (parameter_bool("screen_enabled", false)) this.publish_track(await this.create_screen_track())
- }
-
- publish_track(t: TrackHandle) {
- this.room.remote_users.forEach(u => u.peer.addTrack(t.track))
- this.add_track(t)
- t.addEventListener("ended", () => {
- this.room.remote_users.forEach(u => {
- u.peer.getSenders().forEach(s => {
- if (s.track == t.track) u.peer.removeTrack(s)
- })
- })
- })
- }
-
- add_initial_to_remote(u: RemoteUser) {
- this.tracks.forEach(t => u.peer.addTrack(t.track))
- }
-
- create_controls() {
- const mic_toggle = document.createElement("input")
- const camera_toggle = document.createElement("input")
- const screen_toggle = document.createElement("input")
- mic_toggle.type = camera_toggle.type = screen_toggle.type = "button"
- mic_toggle.value = "Microphone"
- camera_toggle.value = "Camera"
- screen_toggle.value = "Screen"
-
- const create = async (_e: HTMLElement, tp: Promise<TrackHandle>) => {
- log("media", "awaiting track")
- const t = await tp
- log("media", "got track")
- this.publish_track(t)
- }
-
- mic_toggle.addEventListener("click", () => create(mic_toggle, this.create_mic_track()))
- camera_toggle.addEventListener("click", () => create(camera_toggle, this.create_camera_track()))
- screen_toggle.addEventListener("click", () => create(screen_toggle, this.create_screen_track()))
-
- const el = document.createElement("div")
- el.classList.add("local-controls")
- el.append(mic_toggle, camera_toggle, screen_toggle)
- document.body.append(el)
- }
-
-
- async create_camera_track() {
- log("media", "requesting user media (camera)")
- const user_media = await window.navigator.mediaDevices.getUserMedia({ video: true })
- return new TrackHandle(user_media.getVideoTracks()[0], true)
- }
- async create_screen_track() {
- log("media", "requesting user media (screen)")
- const user_media = await window.navigator.mediaDevices.getDisplayMedia({ video: true })
- return new TrackHandle(user_media.getVideoTracks()[0], true)
- }
- async create_mic_track() {
- log("media", "requesting user media (audio)")
- const use_rnnoise = parameter_bool("rnnoise", true)
- const audio_contraints = use_rnnoise ? {
- channelCount: { ideal: 1 },
- noiseSuppression: { ideal: false },
- echoCancellation: { ideal: true },
- autoGainControl: { ideal: false },
- } : true;
-
- const user_media = await window.navigator.mediaDevices.getUserMedia({ audio: audio_contraints })
- const context = new AudioContext()
- const source = context.createMediaStreamSource(user_media)
- const destination = context.createMediaStreamDestination()
- const gain = context.createGain()
- gain.gain.value = this.default_gain
- this.mic_gain = gain
-
- let rnnoise: RNNoiseNode;
- if (use_rnnoise) {
- rnnoise = await get_rnnoise_node(context)
- source.connect(rnnoise)
- rnnoise.connect(gain)
- } else {
- source.connect(gain)
- }
- gain.connect(destination)
-
- const t = new TrackHandle(destination.stream.getAudioTracks()[0], true)
-
- t.addEventListener("ended", () => {
- user_media.getTracks().forEach(t => t.stop())
- source.disconnect()
- if (rnnoise) rnnoise.disconnect()
- gain.disconnect()
- destination.disconnect()
- this.mic_gain = undefined
- })
-
- return t
- }
-}
diff --git a/source/client/logger.ts b/source/client/logger.ts
deleted file mode 100644
index e00b1d0..0000000
--- a/source/client/logger.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-/// <reference lib="dom" />
-
-const log_tag_color = {
- "*": "#FF4444",
- webrtc: "#FF44FF",
- media: "#FFFF44",
- ws: "#44FFFF",
- rnnoise: "#2222FF",
- usermodel: "#44FF44",
- error: "#FF0000",
-}
-export type LogTag = keyof typeof log_tag_color
-
-let logger_container: HTMLDivElement
-
-// TODO maybe log time aswell
-// deno-lint-ignore no-explicit-any
-export function log(tag: LogTag, message: string, ...data: any[]) {
- for (let i = 0; i < data.length; i++) {
- const e = data[i];
- if (e instanceof MediaStreamTrack) data[i] = `(${e.kind}) ${e.id}`
- }
- console.log(`%c[${tag}] ${message}`, "color:" + log_tag_color[tag], ...data);
-
- if (logger_container) {
- const e = document.createElement("p")
- e.classList.add("logger-line")
- e.textContent = `[${tag}] ${message}`
- e.style.color = log_tag_color[tag]
- logger_container.append(e)
- setTimeout(() => {
- e.remove()
- }, tag == "error" ? 60000 : 6000)
- }
-}
-
-globalThis.addEventListener("load", () => {
- const d = document.createElement("div")
- d.classList.add("logger-container")
- document.body.append(d)
- logger_container = d
-
- // clear the console every hour so logs dont accumulate
- setInterval(() => console.clear(), 1000 * 60 * 60)
-})
-
-globalThis.onerror = (_ev, source, line, col, err) => {
- log("error", `${err?.name} ${err?.message}`, err)
- log("error", `on ${source}:${line}:${col}`, err)
-} \ No newline at end of file
diff --git a/source/client/menu.ts b/source/client/menu.ts
deleted file mode 100644
index 1401b42..0000000
--- a/source/client/menu.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Room } from "./room.ts";
-
-export function create_menu(room?: Room) {
- const menu = document.createElement("div")
- menu.classList.add("menu-overlay")
- document.body.append(menu)
-
- const item = (name: string, cb: (() => void) | string) => {
- const p = document.createElement("p")
- const a = document.createElement("a")
- a.classList.add("menu-item")
- a.textContent = name
- if (typeof cb == "string") a.href = cb
- else a.addEventListener("click", cb), a.href = "#"
- p.append(a)
- return p
- }
-
- if (room) menu.append(
- item("Settings", () => alert("todo, refer to the url parameters in the docs for now"))
- )
- menu.append(
- item("Licence", "/licence"),
- item("Sources / Documentation", "https://codeberg.org/metamuffin/keks-meet"),
- )
-} \ No newline at end of file
diff --git a/source/client/remote_user.ts b/source/client/remote_user.ts
deleted file mode 100644
index a0fdeaf..0000000
--- a/source/client/remote_user.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-/// <reference lib="dom" />
-
-import { servers } from "./index.ts"
-import { log } from "./logger.ts"
-import { Room } from "./room.ts"
-import { TrackHandle } from "./track_handle.ts";
-import { User } from "./user.ts"
-
-export class RemoteUser extends User {
- peer: RTCPeerConnection
- negotiation_busy = false
-
- constructor(room: Room, name: string) {
- super(room, name)
- log("usermodel", `added remote user: ${name}`)
- this.peer = new RTCPeerConnection(servers)
- this.peer.onicecandidate = ev => {
- if (!ev.candidate) return
- room.websocket_send({ ice_candiate: ev.candidate.toJSON(), receiver: this.name })
- }
- this.peer.ontrack = ev => {
- const t = ev.track
- log("media", `remote track: ${this.name}`, t)
- this.add_track(new TrackHandle(t))
- }
- this.peer.onnegotiationneeded = async () => {
- log("webrtc", `negotiation needed: ${this.name}`)
- while (this.negotiation_busy) {
- await new Promise<void>(r => setTimeout(() => r(), 100))
- }
- this.offer()
- }
- }
-
- async offer() {
- this.negotiation_busy = true
- const offer_description = await this.peer.createOffer()
- await this.peer.setLocalDescription(offer_description)
- const offer = { type: offer_description.type, sdp: offer_description.sdp }
- log("webrtc", `sent offer: ${this.name}`, { a: offer })
- this.room.websocket_send({ receiver: this.name, offer })
- }
- async on_offer(offer: RTCSessionDescriptionInit) {
- this.negotiation_busy = true
- log("webrtc", `got offer: ${this.name}`, { a: offer })
- const offer_description = new RTCSessionDescription(offer)
- await this.peer.setRemoteDescription(offer_description)
- this.answer()
- }
- async answer() {
- const answer_description = await this.peer.createAnswer()
- await this.peer.setLocalDescription(answer_description)
- const answer = { type: answer_description.type, sdp: answer_description.sdp }
- log("webrtc", `sent answer: ${this.name}`, { a: answer })
- this.room.websocket_send({ receiver: this.name, answer })
- this.negotiation_busy = false
- }
- async on_answer(answer: RTCSessionDescriptionInit) {
- log("webrtc", `got answer: ${this.name}`, { a: answer })
- const answer_description = new RTCSessionDescription(answer)
- await this.peer.setRemoteDescription(answer_description)
- this.negotiation_busy = false
- }
-
- add_ice_candidate(candidate: RTCIceCandidateInit) {
- this.peer.addIceCandidate(new RTCIceCandidate(candidate))
- }
-
- leave() {
- log("usermodel", `remove remote user: ${this.name}`)
- this.peer.close()
- this.room.el.removeChild(this.el)
- }
-} \ No newline at end of file
diff --git a/source/client/rnnoise.ts b/source/client/rnnoise.ts
deleted file mode 100644
index 7867682..0000000
--- a/source/client/rnnoise.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-/// <reference lib="dom" />
-
-import { log } from "./logger.ts"
-
-declare global {
- class RNNoiseNode extends AudioWorkletNode {
- static register(context: AudioContext): Promise<void>
- constructor(context: AudioContext)
- // deno-lint-ignore no-explicit-any
- onstatus: (data: any) => void
- update(something: boolean): void
- }
-}
-
-
-// TODO fix leak
-export async function get_rnnoise_node(context: AudioContext): Promise<RNNoiseNode> {
- log("rnnoise", "enabled")
- //@ts-ignore asfdasfd
- let RNNoiseNode: typeof RNNoiseNode = window.RNNoiseNode;
-
- let script: HTMLScriptElement;
- if (!RNNoiseNode) {
- log("rnnoise", "loading wasm...")
- script = document.createElement("script")
- script.src = "/_rnnoise/rnnoise-runtime.js"
- script.defer = true
- document.head.appendChild(script)
- //@ts-ignore asdfsfad
- while (!window.RNNoiseNode) await new Promise<void>(r => setTimeout(() => r(), 100))
- //@ts-ignore asfdsadfsafd
- RNNoiseNode = window.RNNoiseNode;
- log("rnnoise", "loaded")
- }
-
- await RNNoiseNode.register(context)
- return new RNNoiseNode(context)
-} \ No newline at end of file
diff --git a/source/client/room.ts b/source/client/room.ts
deleted file mode 100644
index 145fb1b..0000000
--- a/source/client/room.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-/// <reference lib="dom" />
-
-import { log } from "./logger.ts";
-import { RemoteUser } from "./remote_user.ts";
-import { User } from "./user.ts";
-import { LocalUser } from "./local_user.ts";
-import { hex_id, parameter_string } from "./helper.ts";
-import { PacketS, PacketC } from "../packets.ts";
-
-
-export class Room {
- el: HTMLElement
- name: string
- users: Map<string, User> = new Map()
- remote_users: Map<string, RemoteUser> = new Map()
- local_user: LocalUser
- websocket: WebSocket
-
- constructor(name: string) {
- this.name = name
- this.el = document.createElement("div")
- this.el.classList.add("room")
- this.websocket = new WebSocket(`${window.location.protocol.endsWith("s:") ? "wss" : "ws"}://${window.location.host}/signaling/${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, parameter_string("username", `guest-${hex_id()}`))
- }
-
- websocket_send(data: PacketS) {
- log("ws", `-> ${data.receiver ?? "*"}`, data)
- this.websocket.send(JSON.stringify(data))
- }
- websocket_message(packet: PacketC) {
- if (packet.join) {
- log("*", `${this.name} ${packet.sender} joined`);
- const ru = new RemoteUser(this, packet.sender)
- this.local_user.add_initial_to_remote(ru)
- if (!packet.stable) ru.offer()
- this.users.set(packet.sender, ru)
- this.remote_users.set(packet.sender, ru)
- return
- }
- const sender = this.remote_users.get(packet.sender)
- if (!sender) return console.warn(`unknown sender ${packet.sender}`)
- if (packet.leave) {
- log("*", `${this.name} ${packet.sender} left`);
- sender.leave()
- this.users.delete(packet.sender)
- this.remote_users.delete(packet.sender)
- return
- }
- if (!packet.data) return console.warn("dataless packet")
- log("ws", `<- ${packet.sender}: `, packet.data);
- if (packet.data.ice_candiate) sender.add_ice_candidate(packet.data.ice_candiate)
- if (packet.data.offer) sender.on_offer(packet.data.offer)
- if (packet.data.answer) sender.on_answer(packet.data.answer)
- }
- websocket_close() {
- log("ws", "websocket closed");
- setTimeout(() => {
- window.location.reload()
- }, 1000)
- }
- websocket_open() {
- log("ws", "websocket opened");
- this.websocket.send(this.local_user.name)
- setInterval(() => this.websocket_send({}), 30000) // stupid workaround for nginx disconnection inactive connections
- }
-} \ No newline at end of file
diff --git a/source/client/track_handle.ts b/source/client/track_handle.ts
deleted file mode 100644
index 98b2b2f..0000000
--- a/source/client/track_handle.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/// <reference lib="dom" />
-
-export class TrackHandle extends EventTarget {
- constructor(
- public track: MediaStreamTrack,
- public local = false
- ) {
- super()
- track.onended = () => this.dispatchEvent(new CustomEvent("ended"))
- // TODO research how onmute and onunmute behave
- track.onmute = () => this.dispatchEvent(new CustomEvent("ended")) // onmute seems to be called when the remote ends the track
- track.onunmute = () => this.dispatchEvent(new CustomEvent("started"))
-
- this.addEventListener("ended", () => {
- // drop all references to help gc
- track.onunmute = track.onmute = track.onended = null
- })
- }
-
- get kind() { return this.track.kind }
- get label() { return this.track.label }
- get muted() { return this.track.muted }
- get id() { return this.track.id }
-
- end() { this.track.stop(); this.dispatchEvent(new CustomEvent("ended")) }
-}
diff --git a/source/client/user.ts b/source/client/user.ts
deleted file mode 100644
index bda875f..0000000
--- a/source/client/user.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-/// <reference lib="dom" />
-
-import { log } from "./logger.ts"
-import { Room } from "./room.ts"
-import { TrackHandle } from "./track_handle.ts";
-
-
-export abstract class User {
- name: string
- room: Room
-
- el: HTMLElement
-
- local = false
-
- protected tracks: Set<TrackHandle> = new Set()
-
- constructor(room: Room, name: string) {
- 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()
- }
-
- add_track(t: TrackHandle) {
- this.tracks.add(t)
- this.create_track_element(t)
- t.addEventListener("ended", () => {
- log("media", "track ended", t)
- this.tracks.delete(t)
- })
- t.addEventListener("mute", () => {
- log("media", "track muted", t)
- })
- t.addEventListener("unmute", () => {
- log("media", "track unmuted", t)
- })
- }
-
- 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")
- info_el.append(name_el)
- this.el.append(info_el)
- }
-
- create_track_element(t: TrackHandle) {
- const is_video = t.kind == "video"
- const media_el = is_video ? document.createElement("video") : document.createElement("audio")
- const stream = new MediaStream([t.track])
- media_el.srcObject = stream
- media_el.classList.add("media")
- media_el.autoplay = true
- media_el.controls = true
-
- if (this.local) media_el.muted = true
-
-
- const el = document.createElement("div")
- if (t.local) {
- const end_button = document.createElement("button")
- end_button.textContent = "End"
- end_button.addEventListener("click", () => {
- t.end()
- })
- el.append(end_button)
- }
- el.append(media_el)
- this.el.append(el)
- t.addEventListener("ended", () => {
- media_el.srcObject = null
- el.remove()
- })
- }
-} \ No newline at end of file
diff --git a/source/packets.ts b/source/packets.ts
deleted file mode 100644
index f42d99b..0000000
--- a/source/packets.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-
-// copy pasted from dom.lib.d.ts because it can not be referenced in the server.
-type F_RTCSdpType = "answer" | "offer" | "pranswer" | "rollback";
-interface F_RTCSessionDescriptionInit { sdp?: string; type: F_RTCSdpType; }
-interface F_RTCIceCandidateInit { candidate?: string; sdpMLineIndex?: number | null; sdpMid?: string | null; usernameFragment?: string | null; }
-
-export interface PacketC {
- sender: string,
- data?: PacketS,
- join?: boolean, // user just joined
- leave?: boolean, // user left
- stable?: boolean // user "joined" because you joined aka. user was already there
-}
-export interface PacketS {
- receiver?: string
- ice_candiate?: F_RTCIceCandidateInit
- offer?: F_RTCSessionDescriptionInit
- answer?: F_RTCSessionDescriptionInit
-}
-
-
diff --git a/source/server/index.ts b/source/server/index.ts
deleted file mode 100644
index de2ba4e..0000000
--- a/source/server/index.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-import { Application, Router, RouterContext, send } from "https://deno.land/x/oak@v10.4.0/mod.ts";
-import { api } from "./room.ts";
-import { bundle } from "https://deno.land/x/emit@0.1.1/mod.ts";
-
-const app = new Application()
-const root = new Router()
-
-
-root.get("/", async c => { await c.send({ path: "index.html", root: `${Deno.cwd()}/public` }) })
-root.get("/room/:id", async c => { await c.send({ path: "index.html", root: `${Deno.cwd()}/public` }) })
-
-root.get("/licen(c|s)e", async c => {
- c.response.body = await Deno.readTextFile("LICENCE")
- c.response.headers.set("Content-Type", "text/plain")
-})
-
-root.get("/favicon.ico", c => { c.response.status = 204 })
-
-// deno-lint-ignore no-explicit-any
-function respondWithType(mimeType: string, f: () => string): (c: RouterContext<any, any, any>) => void {
- return c => {
- c.response.headers.set("Content-Type", mimeType)
- c.response.body = f()
- }
-}
-
-let bundle_code = ""
-root.get("/bundle.js", respondWithType("application/javascript", () => bundle_code))
-
-root.use(api.routes())
-
-function mountFilesystem(r: Router, route: string, path: string) {
- r.get(route + "/(.*)", async (context) => {
- console.log(context.request.url.pathname.substring(route.length));
- await send(context, context.request.url.pathname.substring(route.length), { root: Deno.cwd() + path });
- })
-}
-
-mountFilesystem(root, "/_style", "/public/style")
-mountFilesystem(root, "/_rnnoise", "/public/rnnoise")
-
-app.use(root.routes())
-app.use(root.allowedMethods())
-
-app.addEventListener("listen", ({ hostname, port, secure }) => {
- console.log(`listening on: ${secure ? "https://" : "http://"}${hostname}:${port}`);
-});
-
-app.listen({
- hostname: Deno.env.get("HOSTNAME") ?? "127.0.0.1",
- port: parseInt(Deno.env.get("PORT") ?? "8080")
-});
-
-
-let refresh_needed = false
-let refresh_pending = false
-async function refresh() {
- refresh_needed = true
- if (refresh_pending) return
- refresh_needed = false
- refresh_pending = true
-
- try {
- const { code } = await bundle("source/client/index.ts", { compilerOptions: { checkJs: false } })
- bundle_code = code
- } catch (e) { console.error(e) }
-
- refresh_pending = false
- if (refresh_needed) refresh()
-}
-
-refresh()
-for await (const event of Deno.watchFs("source/client")) {
- if (event.kind == "modify" || event.kind == "create") {
- refresh()
- }
-}
diff --git a/source/server/room.ts b/source/server/room.ts
deleted file mode 100644
index d9c1a25..0000000
--- a/source/server/room.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import { Router } from "https://deno.land/x/oak@v10.4.0/router.ts";
-import { PacketC, PacketS } from "../packets.ts";
-
-export const api = new Router()
-
-type Room = Map<string, WebSocket>
-const rooms: Map<string, Room> = new Map()
-
-function send_packet(ws: WebSocket, packet: PacketC) {
- ws.send(JSON.stringify(packet))
-}
-
-api.get("/signaling/:id", c => {
- const ws = c.upgrade()
-
- const room_name = c.params.id
- const room: Room = rooms.get(c.params.id) ?? new Map()
- let initialized = false
- let user_name = ""
-
- const init = (n: string) => {
- if (room.get(n)) return ws.close()
- initialized = true
- user_name = n
- rooms.set(c.params.id, room)
- room.forEach(uws => send_packet(uws, { sender: user_name, join: true }))
- room.forEach((_, uname) => send_packet(ws, { sender: uname, join: true, stable: true }))
- room.set(user_name, ws)
- console.log(`[${room_name}] ${user_name} joined`)
- }
- ws.onclose = () => {
- room.delete(user_name)
- room.forEach(uws => send_packet(uws, { sender: user_name, leave: true }))
- if (room.size == 0) rooms.delete(room_name)
- console.log(`[${room_name}] ${user_name} left`)
- }
- ws.onmessage = ev => {
- const message = ev.data.toString()
- if (!initialized) return init(message)
- let in_packet: PacketS;
- try { in_packet = JSON.parse(message) }
- catch (_e) { return }
-
- if (JSON.stringify(in_packet) == "{}") return // drop ping
-
- console.log(`[${room_name}] ${user_name} -> ${in_packet.receiver ?? "*"}: ${message.substr(0, 100)}`)
- const out_packet: PacketC = { sender: user_name, data: in_packet }
-
- if (in_packet.receiver) {
- const rws = room.get(in_packet.receiver)
- if (rws) send_packet(rws, out_packet)
- } else {
- room.forEach((uws, uname) => {
- if (uname != user_name) send_packet(uws, out_packet)
- })
- }
- }
-})