//@ts-check import JadefinIntegrity from '../JadefinIntegrity.js'; import Jadefin from "../Jadefin.js"; import JadefinMod from "../JadefinMod.js"; import JadefinModules from "../JadefinModules.js"; import JadefinUtils from "../JadefinUtils.js"; import { rd, rdom, rd$, RDOMListHelper } from "../utils/rdom.js"; export default JadefinIntegrity("ExtrasMenu", import.meta.url, () => new (class ExtrasMenu extends JadefinMod { IN_ALL = 0; IN_CUSTOM = 1; IN_DRAWER = 2; IN_LIBRARY = 4; IN_MOVIE = 8; IN_PLAYBACKSETTINGS = 16; IN_PLAYBACKSETTINGS_ADVANCED = 32; _popupId = 0; _items = []; /** @type {typeof this._items} */ items = new Proxy(this._items, { deleteProperty: (target, property) => { delete target[property]; this.update(); return true; }, set: (target, property, value, receiver) => { target[property] = value; this.update(); return true; } }); headerExtrasEl = rd$()` `; drawerExtrasEl = rd$()`
`; drawerVersionEl = rd$()`
Jellyfin
Jadefin
`; constructor() { super(); this.items.push({ name: "Reload", secondaryText: "Fully reload and update Jellyfin", icon: "update", in: this.IN_LIBRARY, cb: async () => { try { await window.caches.delete("embydata"); } catch (e) { } try { await window.caches.delete(`workbox-precache-v2-${JadefinModules.Emby.Page.baseRoute}/`); } catch (e) { } window.location.reload(); } }); this.headerExtrasEl.addEventListener("click", e => this.openExtrasPopup()); } async init(name, url) { await super.init(name, url); await Promise.all([ this.initStyle(), this.initHeaderExtras(), this.initDrawerExtras(), this.initHookActionSheetShow() ]); this.log.i("Ready"); } async initHeaderExtras() { await JadefinUtils.waitUntil(() => document.querySelector(".headerRight")); document.querySelector(".headerRight")?.appendChild(this.headerExtrasEl); } /** * @param {boolean} [silentWarn] */ async initDrawerExtras(silentWarn) { const drawer = document.querySelector(".mainDrawer > .mainDrawer-scrollContainer"); const userMenuOptions = drawer?.querySelector("& > .userMenuOptions"); if (!userMenuOptions) { if (!silentWarn) { this.log.w("Couldn't find mainDrawer / home userMenuOptions, retrying"); } setTimeout(() => this.initDrawerExtras(true), 1500); return; } drawer?.insertBefore(this.drawerExtrasEl, userMenuOptions); drawer?.appendChild(this.drawerVersionEl); const versionJellyfinEl = this.drawerVersionEl.querySelector("#extrasVersionJellyfin"); if (versionJellyfinEl) { versionJellyfinEl.textContent = JadefinModules.ApiClient.appVersion(); } const versionJadefinEl = this.drawerVersionEl.querySelector("#extrasVersionJadefin"); if (versionJadefinEl) { versionJadefinEl.textContent = Jadefin.version; } this.update(); } async initHookActionSheetShow() { await JadefinUtils.waitUntil(() => JadefinModules.actionSheet); const orig = this._actionSheetShowOrig = JadefinModules.actionSheet.show.bind(JadefinModules.actionSheet); JadefinModules.actionSheet.show = (options) => { const optionsOrig = Object.assign({}, options, { items: options.items.slice() }); // Options menu during playback if (!options.dialogClass && options.items.length >= 4 && options.items[0].id == "aspectratio" && options.items[1].id == "playbackrate" && options.items[2].id == "quality" && options.items[3].id == "repeatmode" && // suboffset might not always exist // options.items[4].id == "suboffset" && // options.items[5].id == "stats" && true ) { return new Promise((resolve, reject) => { const currentVisibility = this.IN_PLAYBACKSETTINGS; const items = [ {id: "extrasMenuPopup-all", name: "Advanced..."}, {divider: true}, ...this._items.map((item, i) => Object.assign({ id: i }, item)).filter(item => this.checkVisibility(currentVisibility, item)), ...optionsOrig.items.slice(4) ]; const p = this._actionSheetShow({ positionTo: optionsOrig.positionTo, currentVisibility, items }); p.then(id => { if (id == "extrasMenuPopup-all") { const currentVisibility = this.IN_PLAYBACKSETTINGS_ADVANCED; const items = [ ...optionsOrig.items.slice(0, 4), ...this._items.map((item, i) => Object.assign({ id: i }, item)).filter(item => this.checkVisibility(currentVisibility, item)), {divider: true}, {id: "extrasMenuPopup-back", name: "Back..."} ]; const p = this._actionSheetShow(Object.assign({}, optionsOrig, { currentVisibility, items })); p.then(id => { if (id == "extrasMenuPopup-back") { JadefinModules.actionSheet.show(optionsOrig).then(resolve).catch(reject); return; } if (id && this._items[id]) { reject(); return; } resolve(id); }).catch(reject); return; } if (id && this._items[id]) { reject(); return; } resolve(id); }).catch(reject); }); } return orig(options); }; } openExtrasPopup() { const currentVisibility = JadefinUtils.isInMovie ? this.IN_MOVIE : this.IN_LIBRARY; this._actionSheetShow({ title: "Extras", positionTo: this.headerExtrasEl, currentVisibility, items: this._items.map((item, i) => Object.assign({ id: i }, item)).filter(item => this.checkVisibility(currentVisibility, item)) }); } update() { this.drawerExtrasEl.innerHTML = `

Extras

`; for (let item of this._items) { if (!this.checkVisibility(this.IN_DRAWER, item)) { continue; } let itemEl = rd$()` `; itemEl.addEventListener("click", e => item.cb?.()); item.cbEl?.(itemEl, this.IN_DRAWER); this.drawerExtrasEl.appendChild(itemEl); } } /** * @param {number} current * @param {any} item */ checkVisibility(current, item) { if ((item.in || this.IN_ALL) == this.IN_ALL) { return true; } if ((item.in & this.IN_CUSTOM) == this.IN_CUSTOM) { return item.inCustom(current, item); } return (item.in & current) == current; } /** * @param {{ positionTo: any; currentVisibility: any; items: any; title?: string; dialogClass?: any; }} options */ _actionSheetShow(options) { options.dialogClass = `extrasMenuPopup-${this._popupId++}`; const p = JadefinModules.actionSheet.show(options); p.then(id => { if (!id) { return; } this._items[id]?.cb?.(options.positionTo); }); const dialogEl = document.querySelector(`.${options.dialogClass}`); if (!dialogEl) { this.log.e(`Couldn't find .${options.dialogClass}`); return p; } this.log.i(`Opened .${options.dialogClass}`); this.log.dir(dialogEl); dialogEl.classList.add("extrasMenuPopup"); const dialogButtons = dialogEl.querySelectorAll("button"); for (let i in options.items) { options.items[i].cbEl?.(dialogButtons[i], options.currentVisibility, true); } p.finally(() => { for (let i in options.items) { options.items[i].cbEl?.(dialogButtons[i], options.currentVisibility, false); } }); return p; } })());