aboutsummaryrefslogtreecommitdiff
path: root/web/script/player/sync.ts
diff options
context:
space:
mode:
Diffstat (limited to 'web/script/player/sync.ts')
-rw-r--r--web/script/player/sync.ts163
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))
- }
-}
-