diff options
Diffstat (limited to 'web/script/player/sync.ts')
| -rw-r--r-- | web/script/player/sync.ts | 163 |
1 files changed, 0 insertions, 163 deletions
diff --git a/web/script/player/sync.ts b/web/script/player/sync.ts deleted file mode 100644 index 5f33a8e..0000000 --- a/web/script/player/sync.ts +++ /dev/null @@ -1,163 +0,0 @@ -/* - This file is part of jellything (https://codeberg.org/metamuffin/jellything) - which is licensed under the GNU Affero General Public License (version 3); see /COPYING. - Copyright (C) 2026 metamuffin <metamuffin.org> -*/ -/// <reference lib="dom" /> -import { OVar, e } from "../jshelper/mod.ts"; -import { Logger } from "../jshelper/src/log.ts"; -import { Player } from "./player.ts" - -export function playersync_controls(sync_state: OVar<undefined | Playersync>, player: Player) { - let channel_name: HTMLInputElement; - let channel_name_copy: HTMLInputElement; - return e("div", { class: ["jsp-controlgroup", "jsp-playersync-controls"] }, - e("h3", "Playersync"), - sync_state.map(sync => sync - ? e("div", - e("span", "Sync enabled."), - e("button", "Disable", { - onclick: () => { sync_state.value?.destroy(); sync_state.value = undefined } - }), - e("p", "Session ID: ", - channel_name_copy = e("input", { type: "text", disabled: true, value: sync.name }), - e("button", "content_paste_go", { - class: "icon", - onclick: () => { - player.logger?.log("Session ID copied to clipboard.") - navigator.clipboard.writeText(channel_name_copy.value) - } - }) - ), - e("h4", "Users"), - sync.users.map(users => - e("ul", ...[...users.keys()].map(u => e("li", u))) - ) - ) - : e("div", - channel_name = e("input", { type: "text", placeholder: "someroom:example.org" }), - e("button", "Join", { - onclick: () => { - if (!channel_name.value.length) return - sync_state.value?.destroy() - sync_state.value = new Playersync(player, player.logger!, channel_name.value) - } - }), e("br"), - e("button", "Create new session", { - onclick: () => { - sync_state.value?.destroy() - sync_state.value = new Playersync(player, player.logger!) - } - })) - ) - ) -} - -function get_username() { - return document.querySelector("nav .account .username")?.textContent ?? "Unknown User" -} - -interface Packet { - time?: number, - playing?: boolean, - join?: string, - leave?: string, -} - -export class Playersync { - private ws: WebSocket - private on_destroy: (() => void)[] = [] - - public name: string - public users = new OVar(new Map<string, null>()) - - private cancel_pers: undefined | (() => void) - set_pers(s?: string) { - if (this.cancel_pers) this.cancel_pers(), this.cancel_pers = undefined - if (s) this.cancel_pers = this.logger?.log_persistent(s) - } - - constructor(private player: Player, private logger: Logger<string>, private channel_name?: string) { - this.set_pers("Playersync enabling...") - - channel_name ??= Math.random().toString(16).padEnd(5, "0").substring(2).substring(0, 6) - let [localpart, remotepart, port] = channel_name.split(":") - if (!remotepart?.length) remotepart = globalThis.location.host - if (port) remotepart += ":" + port - this.name = localpart + ":" + remotepart - - this.ws = new WebSocket(`${globalThis.location.protocol.endsWith("s:") ? "wss" : "ws"}://${remotepart}/playersync/${encodeURIComponent(localpart)}`) - this.on_destroy.push(() => this.ws.close()) - - this.ws.onopen = () => { - this.set_pers() - this.logger.log(`Playersync connected.`) - this.send({ join: get_username() }) - } - this.ws.onerror = () => { - this.set_pers(`Playersync websocket error.`) - } - this.ws.onclose = () => { - this.set_pers(`Playersync websocket closed.`) - } - - let last_time = 0; - this.ws.onmessage = ev => { - const packet: Packet = JSON.parse(ev.data) - console.log("playersync recv", packet); - if (packet.time !== undefined) { - this.player.seek(packet.time) - last_time = packet.time - } - if (packet.playing === true) this.player.play() - if (packet.playing === false) this.player.pause() - if (packet.join) { - this.logger.log(`${packet.join} joined.`) - this.users.value.set(packet.join, null) - this.users.change() - } - if (packet.leave) { - this.logger.log(`${packet.leave} left.`) - this.users.value.delete(packet.leave) - this.users.change() - } - } - - let cb: () => void - - const send_time = () => { - const time = this.player.video.currentTime - if (Math.abs(last_time - time) < 0.01) return - this.send({ time: this.player.video.currentTime }) - } - - player.video.addEventListener("play", cb = () => { - send_time() - this.send({ playing: true }) - }) - this.on_destroy.push(() => player.video.removeEventListener("play", cb)) - - player.video.addEventListener("pause", cb = () => { - this.send({ playing: false }) - send_time() - }) - this.on_destroy.push(() => player.video.removeEventListener("pause", cb)) - - player.video.addEventListener("seeking", cb = () => { - send_time() - }) - this.on_destroy.push(() => player.video.removeEventListener("seeking", cb)) - } - - destroy() { - this.set_pers() - this.logger.log("Playersync disabled.") - this.on_destroy.forEach(f => f()) - } - - send(p: Packet) { - console.log("playersync send", p); - this.ws.send(JSON.stringify(p)) - } -} - |