diff options
Diffstat (limited to 'web/script')
-rw-r--r-- | web/script/js-player.js | 125 | ||||
-rw-r--r-- | web/script/main.ts | 1 | ||||
-rw-r--r-- | web/script/playerconf-copy-url.js | 32 | ||||
-rw-r--r-- | web/script/transition.js | 75 |
4 files changed, 233 insertions, 0 deletions
diff --git a/web/script/js-player.js b/web/script/js-player.js new file mode 100644 index 0000000..7c3bb43 --- /dev/null +++ b/web/script/js-player.js @@ -0,0 +1,125 @@ +/* + 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 <metamuffin.org> +*/ + +globalThis.addEventListener("load", () => { + for (const e of document.getElementsByTagName("video")) + patch_video(e) +}) + +function patch_video(e) { + // move the video to a div + const d = document.createElement("div") + const p = e.parentElement + p.removeChild(e) + d.appendChild(e) + p.prepend(d) + + e.removeAttribute("controls") + d.classList.add("js-player") + + const controls = document.createElement("div") + controls.classList.add("controls") + + const pause_button = document.createElement("button") + pause_button.textContent = "|>" + + const fullscreen_button = document.createElement("button") + fullscreen_button.textContent = "X" + + const status_display = document.createElement("p") + status_display.classList.add("status") + + const pri = document.createElement("div") + pri.classList.add("pri") + const pri_current = document.createElement("div") + pri_current.style.width = "0px" + pri_current.classList.add("pri-current") + pri.append(pri_current) + + controls.append(pause_button, status_display, pri, fullscreen_button) + d.append(controls) + + + e.onloadedmetadata = () => { + + } + e.ondurationchange = () => { + + } + e.ontimeupdate = () => { + status_display.innerHTML = display_time(e.currentTime) + "<br/>" + display_time(e.currentTime - e.duration); // TODO can we have <br> with textContent?! + pri_current.style.width = (e.currentTime / e.duration * 100) + "%" + } + e.onplay = () => { + pause_button.textContent = "..." + } + e.onwaiting = () => { + pause_button.textContent = "..." + } + e.onplaying = () => { + pause_button.textContent = "||" + } + e.onpause = () => { + pause_button.textContent = "|>" + } + const toggle_playing = () => e.paused ? e.play() : e.pause() + + mouse_idle(e, 1000, idle => { + controls.style.opacity = idle ? 0 : 1 + e.style.cursor = idle ? "none" : "default" + }) + + e.addEventListener("click", toggle_playing) + pause_button.addEventListener("click", toggle_playing) + fullscreen_button.addEventListener("click", () => { + if (document.fullscreenElement) document.exitFullscreen() + else document.documentElement.requestFullscreen() + }) + const seek_ev = ev => { + const r = pri.getBoundingClientRect() + const p = (ev.clientX - r.left) / (r.right - r.left) + e.currentTime = p * e.duration + } + pri.addEventListener("mousedown", ev => { + seek_ev(ev) + }) + document.body.addEventListener("keydown", k => { + if (k.code == "Period") e.seekToNextFrame() + else if (k.code == "Space") toggle_playing() + else if (k.code == "ArrowLeft") e.currentTime -= 5 + else if (k.code == "ArrowRight") e.currentTime += 5 + else if (k.code == "ArrowUp") e.currentTime -= 60 + else if (k.code == "ArrowDown") e.currentTime += 60 + else return; + k.preventDefault() + }) +} + +function mouse_idle(e, timeout, cb) { + let ct; + 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) { + 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" : "") +} diff --git a/web/script/main.ts b/web/script/main.ts new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/web/script/main.ts @@ -0,0 +1 @@ + diff --git a/web/script/playerconf-copy-url.js b/web/script/playerconf-copy-url.js new file mode 100644 index 0000000..49f27fd --- /dev/null +++ b/web/script/playerconf-copy-url.js @@ -0,0 +1,32 @@ + +globalThis.addEventListener("load", () => { + for (const e of document.getElementsByClassName("playerconf")) + patch_playerconf(e) +}) + +function patch_playerconf(form) { + const submit = form.lastChild + + const copyurl = document.createElement("button") + const d = document.createElement("div") + form.removeChild(submit) + d.appendChild(submit) + d.appendChild(copyurl) + form.append(d) + + copyurl.textContent = "Copy Stream URL" + d.style.gridArea = "b" + d.style.width = "100%" + copyurl.style.width = "5em" + + copyurl.addEventListener("click", ev => { + const session = document.cookie.split(";").map(e => e.trim().split("=")).find(e => e[0] == "session")[1] + ev.preventDefault() + const fd = new FormData(form) + const sp = ["v", "a", "s"].map(k => fd.get(k)).filter(k => k != "").flat() + const url = `${window.location.protocol}//${window.location.host}/n/${window.location.pathname.split("/")[2]}/stream?tracks=${sp}&session=${session}` + navigator.clipboard.writeText(url) + copyurl.textContent = "Copied" + setTimeout(() => copyurl.textContent = "Copy Stream URL", 1000) + }) +} diff --git a/web/script/transition.js b/web/script/transition.js new file mode 100644 index 0000000..7d39176 --- /dev/null +++ b/web/script/transition.js @@ -0,0 +1,75 @@ +/* + 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 <metamuffin.org> +*/ +/// <reference lib="dom" /> + +const duration = 0.2 +globalThis.addEventListener("load", () => { + patch_page() +}) + +globalThis.addEventListener("popstate", (_e) => { + transition_to(window.location.href, true) + // transition_to(_e.state.href, true) +}) + +function patch_page() { + document.querySelectorAll("a").forEach(el => { + el.addEventListener("click", async ev => { + ev.preventDefault() + await transition_to(el.href) + }) + }) +} + +async function transition_to(href, back) { + const trigger_load = prepare_load(href, back) + await fade(false) + trigger_load() +} + +function prepare_load(href, back) { + const r_promise = fetch(href) + return async () => { + let rt = "" + try { + const r = await r_promise + if (!r.ok) return document.body.innerHTML = "<h1>error</h1>" + rt = await r.text() + } catch (e) { + console.error(e) + return + } + const [head, body] = rt.split("<head>")[1].split("</head>") + document.head.innerHTML = head + document.body.outerHTML = body + fade(true) + // if (!back) window.history.pushState({href}, "", href) + if (!back) window.history.pushState({}, "", href) + patch_page() + } +} + +function fade(dir) { + const overlay = document.createElement("div") + overlay.style.position = "absolute" + overlay.style.left = "0px" + overlay.style.top = "0px" + overlay.style.width = "100vw" + overlay.style.height = "100vh" + overlay.style.backgroundColor = dir ? "black" : "transparent" + overlay.style.transition = `background-color ${duration}s` + overlay.style.zIndex = 99999; + setTimeout(() => { + overlay.style.backgroundColor = dir ? "transparent" : "black" + }, 0) + document.body.appendChild(overlay) + return new Promise(res => { + setTimeout(() => { + if (dir) document.body.removeChild(overlay) + res() + }, duration * 1000) + }) +}
\ No newline at end of file |