From b6139239e35d05621603a2b419bff4c0dc9cdf40 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sat, 21 Oct 2023 20:43:16 +0200 Subject: port js transitions to ts --- web/script/main.ts | 1 + web/script/transition.js | 75 ------------------------------------------------ web/script/transition.ts | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 75 deletions(-) delete mode 100644 web/script/transition.js create mode 100644 web/script/transition.ts (limited to 'web/script') diff --git a/web/script/main.ts b/web/script/main.ts index b59f7af..e2a5045 100644 --- a/web/script/main.ts +++ b/web/script/main.ts @@ -4,3 +4,4 @@ Copyright (C) 2023 metamuffin */ import "./player/mod.ts" +import "./transition.ts" diff --git a/web/script/transition.js b/web/script/transition.js deleted file mode 100644 index 7d39176..0000000 --- a/web/script/transition.js +++ /dev/null @@ -1,75 +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) 2023 metamuffin -*/ -/// - -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 = "

error

" - rt = await r.text() - } catch (e) { - console.error(e) - return - } - const [head, body] = rt.split("")[1].split("") - 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 diff --git a/web/script/transition.ts b/web/script/transition.ts new file mode 100644 index 0000000..aa172f7 --- /dev/null +++ b/web/script/transition.ts @@ -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 +*/ +/// + +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: string, back?: boolean) { + const trigger_load = prepare_load(href, back) + await fade(false) + trigger_load() +} + +function prepare_load(href: string, back?: boolean) { + const r_promise = fetch(href) + return async () => { + let rt = "" + try { + const r = await r_promise + if (!r.ok) return document.body.innerHTML = "

error

" + rt = await r.text() + } catch (e) { + console.error(e) + return + } + const [head, body] = rt.split("")[1].split("") + 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: boolean) { + 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 -- cgit v1.2.3-70-g09d2 From 539e48e3547040c05a84abc796b1778ab5472a8d Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sat, 21 Oct 2023 20:47:30 +0200 Subject: back button only when js --- server/src/routes/ui/layout.rs | 6 +----- server/src/routes/ui/node.rs | 1 - server/src/routes/ui/player.rs | 1 - web/script/backbutton.ts | 8 ++++++++ web/script/main.ts | 1 + 5 files changed, 10 insertions(+), 7 deletions(-) create mode 100644 web/script/backbutton.ts (limited to 'web/script') diff --git a/server/src/routes/ui/layout.rs b/server/src/routes/ui/layout.rs index 1c47247..cdac09c 100644 --- a/server/src/routes/ui/layout.rs +++ b/server/src/routes/ui/layout.rs @@ -27,7 +27,7 @@ use rocket::{ use std::io::Cursor; markup::define! { - Layout<'a, Main: Render>(title: String, main: Main, class: Option<&'a str>, session: Option, show_back: bool) { + Layout<'a, Main: Render>(title: String, main: Main, class: Option<&'a str>, session: Option) { @markup::doctype() html { head { @@ -37,7 +37,6 @@ markup::define! { } body[class=class.unwrap_or("")] { nav { - @if *show_back { a[href="javascript:history.back()"] { "<- Back" } } " " h1 { a[href="/"] { @CONF.brand } } " " @if let Some(_) = session { a[href=uri!(r_library_node("library"))] { "My Library" } " " @@ -80,7 +79,6 @@ pub type DynLayoutPage<'a> = LayoutPage>; pub struct LayoutPage { pub title: String, pub class: Option<&'static str>, - pub show_back: bool, pub content: T, } @@ -90,7 +88,6 @@ impl Default for LayoutPage> { class: None, content: markup::new!(), title: String::new(), - show_back: false, } } } @@ -103,7 +100,6 @@ impl<'r, Main: Render> Responder<'r, 'static> for LayoutPage
{ let session = block_on(req.guard::>()).unwrap(); let mut out = String::new(); Layout { - show_back: self.show_back, main: self.content, title: self.title, class: self.class.as_deref(), diff --git a/server/src/routes/ui/node.rs b/server/src/routes/ui/node.rs index 773daa4..6256c77 100644 --- a/server/src/routes/ui/node.rs +++ b/server/src/routes/ui/node.rs @@ -72,7 +72,6 @@ pub async fn r_library_node_filter<'a>( Ok(Either::Left(LayoutPage { title: node.title.to_string(), - show_back: true, //- !matches!(node.kind, NodeKind::Collection), content: markup::new! { @NodePage { node: &node, id: &id, children: &children, filter: &filter } }, diff --git a/server/src/routes/ui/player.rs b/server/src/routes/ui/player.rs index 4a636e6..d3a3342 100644 --- a/server/src/routes/ui/player.rs +++ b/server/src/routes/ui/player.rs @@ -70,7 +70,6 @@ pub fn r_player<'a>( } @conf }, - show_back: true, ..Default::default() }) } diff --git a/web/script/backbutton.ts b/web/script/backbutton.ts new file mode 100644 index 0000000..c1225c0 --- /dev/null +++ b/web/script/backbutton.ts @@ -0,0 +1,8 @@ +import { e } from "./jshelper/mod.ts"; + +globalThis.addEventListener("DOMContentLoaded", () => { + document.getElementsByTagName("nav").item(0)?.prepend( + e("a", "<- Back", { onclick() { history.back() } }) + ) +}) + diff --git a/web/script/main.ts b/web/script/main.ts index e2a5045..dd168d5 100644 --- a/web/script/main.ts +++ b/web/script/main.ts @@ -5,3 +5,4 @@ */ import "./player/mod.ts" import "./transition.ts" +import "./backbutton.ts" -- cgit v1.2.3-70-g09d2 From 704d65109a0a249583e49e600fef848934cfc13e Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sat, 21 Oct 2023 20:59:02 +0200 Subject: reenable js transitions --- server/src/routes/ui/style.rs | 1 + web/js-transition.css | 16 ++++++++++++++++ web/script/transition.ts | 13 ++++++------- 3 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 web/js-transition.css (limited to 'web/script') diff --git a/server/src/routes/ui/style.rs b/server/src/routes/ui/style.rs index 1f92690..b8a5fdb 100644 --- a/server/src/routes/ui/style.rs +++ b/server/src/routes/ui/style.rs @@ -37,6 +37,7 @@ fn css_bundle() -> String { "nodepage.css", "nodecard.css", "js-player.css", + "js-transition.css", "forms.css" ) } diff --git a/web/js-transition.css b/web/js-transition.css new file mode 100644 index 0000000..54a899f --- /dev/null +++ b/web/js-transition.css @@ -0,0 +1,16 @@ +@keyframes jst-fadein { + from { + background-color: transparent; + } + to { + background-color: black; + } +} +@keyframes jst-fadeout { + from { + background-color: black; + } + to { + background-color: transparent; + } +} diff --git a/web/script/transition.ts b/web/script/transition.ts index aa172f7..809abd6 100644 --- a/web/script/transition.ts +++ b/web/script/transition.ts @@ -5,7 +5,7 @@ */ /// -const duration = 0.2 +const duration = 200 globalThis.addEventListener("load", () => { patch_page() }) @@ -60,16 +60,15 @@ function fade(dir: boolean) { 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) + overlay.style.animationName = dir ? "jst-fadeout" : "jst-fadein" + overlay.style.animationFillMode = "forwards" + overlay.style.animationDuration = `${duration}ms` document.body.appendChild(overlay) return new Promise(res => { setTimeout(() => { if (dir) document.body.removeChild(overlay) res() - }, duration * 1000) + }, duration) }) -} \ No newline at end of file +} -- cgit v1.2.3-70-g09d2 From 3a58f62d819ef37be536d259461ce50d54b7c5ac Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sat, 21 Oct 2023 21:12:05 +0200 Subject: transition errors --- web/js-transition.css | 18 ++++++++++++++++++ web/script/transition.ts | 21 ++++++++++----------- 2 files changed, 28 insertions(+), 11 deletions(-) (limited to 'web/script') diff --git a/web/js-transition.css b/web/js-transition.css index 54a899f..867e30c 100644 --- a/web/js-transition.css +++ b/web/js-transition.css @@ -14,3 +14,21 @@ background-color: transparent; } } + +.jst-fade { + position: fixed; + left: 0px; + top: 0px; + width: 100vw; + height: 100vh; + z-index: 100; +} +.jst-error { + position: fixed; + top: 50vh; + left: 50vw; + transform: translate(-50%, -50%); + color: rgb(247, 69, 69); + font-size: large; + z-index: 101; +} diff --git a/web/script/transition.ts b/web/script/transition.ts index 809abd6..747ed70 100644 --- a/web/script/transition.ts +++ b/web/script/transition.ts @@ -5,6 +5,8 @@ */ /// +import { e } from "./jshelper/src/element.ts"; + const duration = 200 globalThis.addEventListener("load", () => { patch_page() @@ -12,7 +14,6 @@ globalThis.addEventListener("load", () => { globalThis.addEventListener("popstate", (_e) => { transition_to(window.location.href, true) - // transition_to(_e.state.href, true) }) function patch_page() { @@ -30,23 +31,26 @@ async function transition_to(href: string, back?: boolean) { trigger_load() } +function show_error(mesg: string) { + document.body.append(e("span", { class: "jst-error" }, mesg)) +} + function prepare_load(href: string, back?: boolean) { const r_promise = fetch(href) return async () => { let rt = "" try { const r = await r_promise - if (!r.ok) return document.body.innerHTML = "

error

" + if (!r.ok) return show_error("Error response. Try again.") rt = await r.text() } catch (e) { - console.error(e) - return + if (e instanceof TypeError) return show_error("Navigation failed. Check your connection.") + return show_error("unknown error when fetching page") } const [head, body] = rt.split("")[1].split("") 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() } @@ -54,13 +58,8 @@ function prepare_load(href: string, back?: boolean) { function fade(dir: boolean) { 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.classList.add("jst-fade") overlay.style.backgroundColor = dir ? "black" : "transparent" - overlay.style.zIndex = "99999"; overlay.style.animationName = dir ? "jst-fadeout" : "jst-fadein" overlay.style.animationFillMode = "forwards" overlay.style.animationDuration = `${duration}ms` -- cgit v1.2.3-70-g09d2 From 1f398d2b082a0f36b070bb5dd8287ab6f855ae78 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sat, 21 Oct 2023 21:18:12 +0200 Subject: dispatch domcontentloaded when trans done --- web/script/transition.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'web/script') diff --git a/web/script/transition.ts b/web/script/transition.ts index 747ed70..5043c18 100644 --- a/web/script/transition.ts +++ b/web/script/transition.ts @@ -50,6 +50,7 @@ function prepare_load(href: string, back?: boolean) { const [head, body] = rt.split("")[1].split("") document.head.innerHTML = head document.body.outerHTML = body + globalThis.dispatchEvent(new Event("DOMContentLoaded")) fade(true) if (!back) window.history.pushState({}, "", href) patch_page() -- cgit v1.2.3-70-g09d2 From 13ceba2544203a3f1bd2402336c0210e51f6801a Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sat, 21 Oct 2023 21:29:39 +0200 Subject: force reload when player unloads --- web/script/player/mod.ts | 3 ++- web/script/transition.ts | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'web/script') diff --git a/web/script/player/mod.ts b/web/script/player/mod.ts index 8473280..ce3c113 100644 --- a/web/script/player/mod.ts +++ b/web/script/player/mod.ts @@ -9,12 +9,13 @@ import { Logger } from "../jshelper/src/log.ts"; import { EncodingProfile } from "./jhls.d.ts"; import { Player } from "./player.ts"; -document.addEventListener("DOMContentLoaded", () => { +globalThis.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() + globalThis.dispatchEvent(new Event("navigationrequiresreload")) initialize_player(main, node_id) } }) diff --git a/web/script/transition.ts b/web/script/transition.ts index 5043c18..6616bc0 100644 --- a/web/script/transition.ts +++ b/web/script/transition.ts @@ -16,9 +16,15 @@ globalThis.addEventListener("popstate", (_e) => { transition_to(window.location.href, true) }) +let disable_transition = false +globalThis.addEventListener("navigationrequiresreload", () => { + disable_transition = true +}) + function patch_page() { document.querySelectorAll("a").forEach(el => { el.addEventListener("click", async ev => { + if (disable_transition) return ev.preventDefault() await transition_to(el.href) }) -- cgit v1.2.3-70-g09d2 From 18823ebd3533f7d978c9725f739b9bc25e51d820 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sun, 22 Oct 2023 15:14:07 +0200 Subject: skippable transition by popstate --- web/script/transition.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'web/script') diff --git a/web/script/transition.ts b/web/script/transition.ts index 6616bc0..eedc72d 100644 --- a/web/script/transition.ts +++ b/web/script/transition.ts @@ -12,8 +12,10 @@ globalThis.addEventListener("load", () => { patch_page() }) -globalThis.addEventListener("popstate", (_e) => { - transition_to(window.location.href, true) +globalThis.addEventListener("popstate", async (_e) => { + const had_disable_transition = disable_transition; + await transition_to(window.location.href, true) + if (had_disable_transition) window.location.reload() }) let disable_transition = false @@ -35,6 +37,7 @@ async function transition_to(href: string, back?: boolean) { const trigger_load = prepare_load(href, back) await fade(false) trigger_load() + disable_transition = false; } function show_error(mesg: string) { -- cgit v1.2.3-70-g09d2 From d729b5199cd8740ef203b509079dd2aed0d70e23 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sun, 22 Oct 2023 15:14:25 +0200 Subject: change url before content load --- web/script/transition.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'web/script') diff --git a/web/script/transition.ts b/web/script/transition.ts index eedc72d..2bf2a7b 100644 --- a/web/script/transition.ts +++ b/web/script/transition.ts @@ -57,11 +57,11 @@ function prepare_load(href: string, back?: boolean) { return show_error("unknown error when fetching page") } const [head, body] = rt.split("")[1].split("") + if (!back) window.history.pushState({}, "", href) document.head.innerHTML = head document.body.outerHTML = body globalThis.dispatchEvent(new Event("DOMContentLoaded")) fade(true) - if (!back) window.history.pushState({}, "", href) patch_page() } } -- cgit v1.2.3-70-g09d2 From 825a69b1866e98db181e5e6dec4e6c879eeac79d Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sun, 22 Oct 2023 15:29:12 +0200 Subject: generalize transitions --- web/script/jshelper | 2 +- web/script/transition.ts | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'web/script') diff --git a/web/script/jshelper b/web/script/jshelper index bd8b233..2d36b07 160000 --- a/web/script/jshelper +++ b/web/script/jshelper @@ -1 +1 @@ -Subproject commit bd8b233316820cd35178085ef132f82279be0504 +Subproject commit 2d36b0762459b8edfc1529827d0c15447edd2669 diff --git a/web/script/transition.ts b/web/script/transition.ts index 2bf2a7b..00ed9a7 100644 --- a/web/script/transition.ts +++ b/web/script/transition.ts @@ -8,14 +8,12 @@ import { e } from "./jshelper/src/element.ts"; const duration = 200 -globalThis.addEventListener("load", () => { +globalThis.addEventListener("DOMContentLoaded", () => { patch_page() }) globalThis.addEventListener("popstate", async (_e) => { - const had_disable_transition = disable_transition; await transition_to(window.location.href, true) - if (had_disable_transition) window.location.reload() }) let disable_transition = false @@ -26,7 +24,6 @@ globalThis.addEventListener("navigationrequiresreload", () => { function patch_page() { document.querySelectorAll("a").forEach(el => { el.addEventListener("click", async ev => { - if (disable_transition) return ev.preventDefault() await transition_to(el.href) }) @@ -34,10 +31,12 @@ function patch_page() { } async function transition_to(href: string, back?: boolean) { + if (disable_transition) return window.location.href = href const trigger_load = prepare_load(href, back) await fade(false) trigger_load() disable_transition = false; + await fade(true) } function show_error(mesg: string) { @@ -61,8 +60,6 @@ function prepare_load(href: string, back?: boolean) { document.head.innerHTML = head document.body.outerHTML = body globalThis.dispatchEvent(new Event("DOMContentLoaded")) - fade(true) - patch_page() } } -- cgit v1.2.3-70-g09d2 From 1e239240a072a14a1af41785477946435eaf2101 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sun, 22 Oct 2023 15:34:56 +0200 Subject: dont show buffering on video end --- web/script/player/player.ts | 1 + web/script/transition.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'web/script') diff --git a/web/script/player/player.ts b/web/script/player/player.ts index 4c1d9fc..062ad59 100644 --- a/web/script/player/player.ts +++ b/web/script/player/player.ts @@ -38,6 +38,7 @@ export class Player { } this.video.onwaiting = () => { console.log("waiting"); + if (this.video.currentTime > this.duration.value - 0.2) return this.set_pers("Playback finished") this.set_pers("Buffering...") this.canplay.value = false; } diff --git a/web/script/transition.ts b/web/script/transition.ts index 00ed9a7..e0ee6f5 100644 --- a/web/script/transition.ts +++ b/web/script/transition.ts @@ -36,7 +36,6 @@ async function transition_to(href: string, back?: boolean) { await fade(false) trigger_load() disable_transition = false; - await fade(true) } function show_error(mesg: string) { @@ -60,6 +59,7 @@ function prepare_load(href: string, back?: boolean) { document.head.innerHTML = head document.body.outerHTML = body globalThis.dispatchEvent(new Event("DOMContentLoaded")) + fade(true) } } -- cgit v1.2.3-70-g09d2 From 13a6887415547f5e3bafec80d5088ff90ae94aa6 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Mon, 23 Oct 2023 21:09:46 +0200 Subject: hard-coded codec support check --- web/script/player/profiles.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'web/script') diff --git a/web/script/player/profiles.ts b/web/script/player/profiles.ts index a768d47..adaa717 100644 --- a/web/script/player/profiles.ts +++ b/web/script/player/profiles.ts @@ -33,19 +33,35 @@ export class ProfileSelector { if (i.subtitles) return this.profiles_subtitles return [] } + remux_supported(track: number): boolean { + // TODO use media capability API + // like so: await navigator.mediaCapabilities.decodingInfo({type:"file",video:{contentType:"video/webm; codecs=av1",bitrate:5*1000*1000/8,framerate:60,height:1080,width:1920}}) + const codec = this.metadata.tracks[track].info.codec + return [ + "V_AV1", + "V_VP8", + "V_VP9", + "A_OPUS", + "A_VORBIS", + "S_TEXT/WEBVTT" + ].includes(codec) + } select_optimal_profile(track: number, profile: OVar) { const profs = this.profile_list_for_track(track) - const co = profile.value?.order ?? -1 + const sup_remux = this.remux_supported(track); + const min_prof = sup_remux ? -1 : 0 + const co = profile.value?.order ?? min_prof const current_bitrate = profile_byterate(profs[co], 5000 * 1000) const next_bitrate = profile_byterate(profs[co - 1], 5000 * 1000) // console.log({ current_bitrate, next_bitrate, co, bandwidth: this.bandwidth.value * 8 }); + if (!sup_remux && !profile.value) profile.value = profs[co]; if (current_bitrate > this.bandwidth.value * PROFILE_DOWN_FAC && co + 1 < profs.length) { console.log("profile up"); profile.value = profs[co + 1] this.log_change(track, profile.value) } - if (next_bitrate < this.bandwidth.value * PROFILE_UP_FAC && co >= 0) { + if (next_bitrate < this.bandwidth.value * PROFILE_UP_FAC && co > min_prof) { console.log("profile down"); profile.value = profs[co - 1] this.log_change(track, profile.value) -- cgit v1.2.3-70-g09d2 From e7096a499a6e2a8a05f0991898f75da9c928dd70 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Mon, 23 Oct 2023 21:41:57 +0200 Subject: player: do actual cap check to see if transcode is required --- web/script/player/mediacaps.ts | 66 ++++++++++++++++++++++++++++++++++++++++++ web/script/player/profiles.ts | 19 ++++-------- web/script/player/track.ts | 2 +- 3 files changed, 72 insertions(+), 15 deletions(-) create mode 100644 web/script/player/mediacaps.ts (limited to 'web/script') diff --git a/web/script/player/mediacaps.ts b/web/script/player/mediacaps.ts new file mode 100644 index 0000000..ff143c0 --- /dev/null +++ b/web/script/player/mediacaps.ts @@ -0,0 +1,66 @@ +import { SourceTrack, SourceTrackKind } from "./jhls.d.ts"; + +const cache = new Map() + +// TODO this testing method makes the assumption, that if the codec is supported on its own, it can be +// TODO arbitrarly combined with others that are supported. in reality this is true but the spec does not gurantee it. + +export async function test_media_capability(track: SourceTrack): Promise { + const cache_key = `${get_track_kind(track.kind)};${track.codec}` + const cached = cache.get(cache_key); + if (cached !== undefined) return cached + const r = await test_media_capability_inner(track) + console.log(`${r ? "positive" : "negative"} media capability test finished for codec=${track.codec}`); + cache.set(cache_key, r) + return r +} +async function test_media_capability_inner(track: SourceTrack) { + if (track.kind.subtitles) { + return track.codec == "V_TEXT/WEBVTT" // TODO: actually implement it + } + let res; + const codec = MASTROSKA_CODEC_MAP[track.codec] + if (!codec) return console.warn(`unknown codec: ${track.codec}`), false + if (track.kind.audio) { + res = await navigator.mediaCapabilities.decodingInfo({ + type: "media-source", + audio: { + contentType: `audio/webm^; codecs=${codec}`, + samplerate: track.kind.audio.sample_rate, + channels: "" + track.kind.audio.channels, + bitrate: 128 * 1000, + } + }) + } + if (track.kind.video) { + res = await navigator.mediaCapabilities.decodingInfo({ + type: "media-source", + video: { + contentType: `video/webm; codecs=${codec}`, + framerate: track.kind.video.fps || 30, + width: track.kind.video.width, + height: track.kind.video.height, + bitrate: 5 * 1000 * 1000 // TODO we dont know this but we should in the future + } + }) + } + return res?.supported ?? false +} + +const MASTROSKA_CODEC_MAP: { [key: string]: string } = { + "V_VP9": "vp9", + "V_VP8": "vp8", + "V_AV1": "av1", + "V_MPEG4/ISO/AVC": "h264", + "V_MPEGH/ISO/HEVC": "h265", + "A_OPUS": "opus", + "A_VORBIS": "vorbis", + "S_TEXT/WEBVTT": "webvtt", +} + +export function get_track_kind(track: SourceTrackKind): "audio" | "video" | "subtitles" { + if (track.audio) return "audio" + if (track.video) return "video" + if (track.subtitles) return "subtitles" + throw new Error("invalid track"); +} diff --git a/web/script/player/profiles.ts b/web/script/player/profiles.ts index adaa717..caa46bd 100644 --- a/web/script/player/profiles.ts +++ b/web/script/player/profiles.ts @@ -1,5 +1,6 @@ import { OVar } from "../jshelper/mod.ts"; import { EncodingProfile, JhlsMetadata } from "./jhls.d.ts"; +import { test_media_capability } from "./mediacaps.ts"; import { Player } from "./player.ts"; const PROFILE_UP_FAC = 0.6 @@ -33,23 +34,13 @@ export class ProfileSelector { if (i.subtitles) return this.profiles_subtitles return [] } - remux_supported(track: number): boolean { - // TODO use media capability API - // like so: await navigator.mediaCapabilities.decodingInfo({type:"file",video:{contentType:"video/webm; codecs=av1",bitrate:5*1000*1000/8,framerate:60,height:1080,width:1920}}) - const codec = this.metadata.tracks[track].info.codec - return [ - "V_AV1", - "V_VP8", - "V_VP9", - "A_OPUS", - "A_VORBIS", - "S_TEXT/WEBVTT" - ].includes(codec) + async remux_supported(track: number): Promise { + return await test_media_capability(this.metadata.tracks[track].info) } - select_optimal_profile(track: number, profile: OVar) { + async select_optimal_profile(track: number, profile: OVar) { const profs = this.profile_list_for_track(track) - const sup_remux = this.remux_supported(track); + const sup_remux = await this.remux_supported(track); const min_prof = sup_remux ? -1 : 0 const co = profile.value?.order ?? min_prof const current_bitrate = profile_byterate(profs[co], 5000 * 1000) diff --git a/web/script/player/track.ts b/web/script/player/track.ts index e2d9d85..4173b12 100644 --- a/web/script/player/track.ts +++ b/web/script/player/track.ts @@ -81,7 +81,7 @@ export class PlayerTrack { async load(index: number) { this.loading.add(index) - this.player.profile_selector.select_optimal_profile(this.track_index, this.profile) + await this.player.profile_selector.select_optimal_profile(this.track_index, this.profile) const url = `/n/${encodeURIComponent(this.node_id)}/stream?format=hlsseg&tracks=${this.track_index}&index=${index}${this.profile.value ? `&profile=${this.profile.value.id}` : ""}`; const buf = await this.player.downloader.download(url) await new Promise(cb => { -- cgit v1.2.3-70-g09d2 From 55f7f06cecd5b6f5661f6f22e8bb3e0448b9713a Mon Sep 17 00:00:00 2001 From: metamuffin Date: Mon, 23 Oct 2023 21:43:42 +0200 Subject: player: fix double-remove of persistent logs --- web/script/player/player.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'web/script') diff --git a/web/script/player/player.ts b/web/script/player/player.ts index 062ad59..acf2a19 100644 --- a/web/script/player/player.ts +++ b/web/script/player/player.ts @@ -21,7 +21,7 @@ export class Player { private cancel_buffering_pers: undefined | (() => void) set_pers(s?: string) { - if (this.cancel_buffering_pers) this.cancel_buffering_pers() + if (this.cancel_buffering_pers) this.cancel_buffering_pers(), this.cancel_buffering_pers = undefined if (s) this.cancel_buffering_pers = this.logger?.log_persistent(s) } -- cgit v1.2.3-70-g09d2