/* 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) 2023 metamuffin */ import { e } from "../jshelper/mod.ts"; import { Player } from "./player.ts"; document.addEventListener("DOMContentLoaded", () => { if (document.body.classList.contains("player")) { if (!globalThis.MediaSource) return alert("Media Source Extension API required") const node_id = globalThis.location.pathname.split("/")[2]; const main = document.getElementById("main")!; document.getElementsByTagName("footer")[0].remove() initialize_player(main, node_id) } }) function initialize_player(el: HTMLElement, node_id: string) { el.innerHTML = "" // clear the body const player = new Player(node_id) const toggle_playing = () => player.playing.value ? player.pause() : player.play() const pri_map = (v: number) => (v / player.duration.value * 100) + "%" let pri_current: HTMLElement; let pri: HTMLElement; const controls = e("div", { class: "jsp-controls" }, player.playing.map(playing => e("button", playing ? "||" : "|>", { onclick: toggle_playing })), e("p", { class: "jsp-status" }, player.position.map(v => e("span", display_time(v))), e("br"), player.position.map(v => e("span", display_time(v - player.duration.value))) ), pri = e("div", { class: "jsp-pri" }, pri_current = e("div", { class: "jsp-pri-current" }), player.tracks.map( tracks => e("div", ...tracks.map((t, i) => t.buffered.map( ranges => e("div", ...ranges.map( r => e("div", { class: ["jsp-pri-buffer", `jsp-pri-buffer-${r.status}`], style: { width: pri_map(r.end - r.start), top: `calc(var(--pribufsize)*${i})`, left: pri_map(r.start) } }) )) ))) ) ), e("button", "X", { onclick() { if (document.fullscreenElement) document.exitFullscreen() else document.documentElement.requestFullscreen() } }) ) player.position.onchangeinit(p => pri_current.style.width = pri_map(p)) const pel = e("div", { class: "jsp" }, player.video, player.buffering_status.map(b => e("div", { class: "jsp-overlay" }, b ? e("p", { class: "jsp-buffering" }, b) : undefined )), controls, ) el.append(pel) mouse_idle(pel, 1000, idle => { controls.style.opacity = idle ? "0" : "1" pel.style.cursor = idle ? "none" : "default" }) player.video.addEventListener("click", toggle_playing) pri.addEventListener("mousedown", ev => { const r = pri.getBoundingClientRect() const p = (ev.clientX - r.left) / (r.right - r.left) player.seek(p * player.duration.value) }) document.body.addEventListener("keydown", k => { if (k.code == "Period") player.pause(), player.frame_forward() if (k.code == "Space") toggle_playing() else if (k.code == "ArrowLeft") player.seek(player.position.value - 5) else if (k.code == "ArrowRight") player.seek(player.position.value + 5) else if (k.code == "ArrowUp") player.seek(player.position.value - 60) else if (k.code == "ArrowDown") player.seek(player.position.value + 60) else return; k.preventDefault() }) } function mouse_idle(e: HTMLElement, timeout: number, cb: (b: boolean) => unknown) { let ct: number; let idle = false e.onmouseleave = () => { clearTimeout(ct) } e.onmousemove = () => { clearTimeout(ct) if (idle) { idle = false cb(idle) } ct = setTimeout(() => { idle = true cb(idle) }, timeout) } } function display_time(t: number): string { if (t < 0) return "-" + display_time(-t) let h = 0, m = 0, s = 0; while (t > 3600) t -= 3600, h++; while (t > 60) t -= 60, m++; while (t > 1) t -= 1, s++; return (h ? h + "h" : "") + (m ? m + "m" : "") + (s ? s + "s" : "") }