|
@@ -1,6 +1,5 @@
|
|
|
// ==UserScript==
|
|
|
// @name BetterYTM
|
|
|
-// @name:de BetterYTM
|
|
|
// @namespace https://github.com/Sv443/BetterYTM#readme
|
|
|
// @version 1.0.0
|
|
|
// @license MIT
|
|
@@ -10,7 +9,7 @@
|
|
|
// @description:de Verbesserungen für YouTube Music
|
|
|
// @match https://music.youtube.com/*
|
|
|
// @match https://www.youtube.com/*
|
|
|
-// @icon https://www.google.com/s2/favicons?domain=music.youtube.com
|
|
|
+// @icon https://raw.githubusercontent.com/Sv443/BetterYTM/main/resources/icon/v2.1_200.png
|
|
|
// @run-at document-start
|
|
|
// @grant GM.getValue
|
|
|
// @grant GM.setValue
|
|
@@ -27,36 +26,74 @@
|
|
|
/* C&D this, Susan 🖕 */
|
|
|
|
|
|
|
|
|
-(async () => {
|
|
|
"use-strict";
|
|
|
|
|
|
-
|
|
|
+(async () => {
|
|
|
/** Set to true to enable debug mode for more output in the JS console */
|
|
|
const dbg = true;
|
|
|
|
|
|
-
|
|
|
-const defaultFeatures = {
|
|
|
- /** Whether arrow keys should skip forwards and backwards by 10 seconds */
|
|
|
- arrowKeySupport: true,
|
|
|
- /** Whether to remove the "Upgrade" / YT Music Premium tab */
|
|
|
- removeUpgradeTab: true,
|
|
|
-
|
|
|
- /** Whether to add a key combination to switch between the YT and YTM sites on a video */
|
|
|
- switchBetweenSites: true,
|
|
|
- /** Adds a button to the media controls bar to search for the current song's lyrics on genius.com in a new tab */
|
|
|
- geniusLyrics: true,
|
|
|
- /** Adds a lyrics button to each song in the queue ("up next" tab) */
|
|
|
- lyricsButtonsOnSongQueue: true,
|
|
|
-
|
|
|
- /** Set to true to remove the watermark under the YTM logo */
|
|
|
- removeWatermark: false,
|
|
|
+// const branch = "main";
|
|
|
+const branch = "develop"; // #DEBUG#
|
|
|
+
|
|
|
+const featInfo = {
|
|
|
+ arrowKeySupport: {
|
|
|
+ desc: "Arrow keys should skip forwards and backwards by 10 seconds?",
|
|
|
+ type: "toggle",
|
|
|
+ default: true,
|
|
|
+ },
|
|
|
+ removeUpgradeTab: {
|
|
|
+ desc: "Remove the \"Upgrade\" / YT Music Premium tab?",
|
|
|
+ type: "toggle",
|
|
|
+ default: true,
|
|
|
+ },
|
|
|
+ switchBetweenSites: {
|
|
|
+ desc: "Add F9 as a hotkey to switch between the YT and YTM sites on a video / song?",
|
|
|
+ type: "toggle",
|
|
|
+ default: true,
|
|
|
+ },
|
|
|
+ geniusLyrics: {
|
|
|
+ desc: "Add a button to the media controls bar to search for the current song's lyrics on genius.com in a new tab?",
|
|
|
+ type: "toggle",
|
|
|
+ default: true,
|
|
|
+ },
|
|
|
+ lyricsButtonsOnSongQueue: {
|
|
|
+ desc: "TODO: Add a lyrics button to each song in the queue (\"up next\" tab)?",
|
|
|
+ type: "toggle",
|
|
|
+ default: true,
|
|
|
+ },
|
|
|
+ volumeSliderSize: {
|
|
|
+ desc: "The width of the volume slider in px",
|
|
|
+ type: "number",
|
|
|
+ min: 10,
|
|
|
+ max: 1000,
|
|
|
+ default: 175,
|
|
|
+ },
|
|
|
+ volumeSliderStep: {
|
|
|
+ desc: "Volume slider sensitivity - the smaller this number, the finer the volume control",
|
|
|
+ type: "slider",
|
|
|
+ min: 1,
|
|
|
+ max: 20,
|
|
|
+ default: 2,
|
|
|
+ },
|
|
|
+ removeWatermark: {
|
|
|
+ desc: "Remove the watermark under the YTM logo?",
|
|
|
+ type: "toggle",
|
|
|
+ default: false,
|
|
|
+ },
|
|
|
};
|
|
|
|
|
|
+/** @type {FeatureConfig} */
|
|
|
+const defaultFeatures = Object.keys(featInfo).reduce((acc, key) => {
|
|
|
+ acc[key] = featInfo[key].default;
|
|
|
+ return acc;
|
|
|
+}, {});
|
|
|
+
|
|
|
const featureConf = await loadFeatureConf();
|
|
|
|
|
|
console.log("bytm load", featureConf);
|
|
|
|
|
|
const features = { ...defaultFeatures, ...featureConf };
|
|
|
+// const features = { ...defaultFeatures };
|
|
|
|
|
|
console.log("bytm save", features);
|
|
|
|
|
@@ -68,6 +105,8 @@ await saveFeatureConf(features);
|
|
|
|
|
|
/** @typedef {"yt"|"ytm"} Domain Constant string representation of which domain this script is currently running on */
|
|
|
|
|
|
+/** @typedef {typeof defaultFeatures} FeatureConfig */
|
|
|
+
|
|
|
|
|
|
//#MARKER init
|
|
|
|
|
@@ -76,10 +115,10 @@ await saveFeatureConf(features);
|
|
|
const triesLimit = 40;
|
|
|
|
|
|
/** Base URL of geniURL */
|
|
|
-const geniURLBaseUrl = "https://api.sv443.net/geniurl";
|
|
|
+const geniUrlBase = "https://api.sv443.net/geniurl";
|
|
|
|
|
|
/** GeniURL endpoint that gives song metadata when provided with a `?q` parameter - [more info](https://api.sv443.net/geniurl) */
|
|
|
-const geniURLSearchTopUrl = `${geniURLBaseUrl}/search/top`;
|
|
|
+const geniURLSearchTopUrl = `${geniUrlBase}/search/top`;
|
|
|
|
|
|
const info = Object.freeze({
|
|
|
name: GM.info.script.name, // eslint-disable-line no-undef
|
|
@@ -92,7 +131,7 @@ function init()
|
|
|
try
|
|
|
{
|
|
|
console.log(`${info.name} v${info.version} - ${info.namespace}`);
|
|
|
- console.log(`Powered by lots of ambition and my song metadata API called geniURL: ${geniURLBaseUrl}`);
|
|
|
+ console.log(`Powered by lots of ambition and my song metadata API called geniURL: ${geniUrlBase}`);
|
|
|
|
|
|
document.addEventListener("DOMContentLoaded", onDomLoad);
|
|
|
}
|
|
@@ -136,6 +175,11 @@ async function onDomLoad()
|
|
|
|
|
|
if(features.lyricsButtonsOnSongQueue)
|
|
|
await addQueueGeniusBtns();
|
|
|
+
|
|
|
+ if(typeof features.volumeSliderSize === "number")
|
|
|
+ setVolSliderSize(features.volumeSliderSize);
|
|
|
+
|
|
|
+ setVolSliderStep();
|
|
|
}
|
|
|
|
|
|
if(["ytm", "yt"].includes(domain))
|
|
@@ -188,15 +232,19 @@ function addMenu()
|
|
|
const menuContainer = document.createElement("div");
|
|
|
menuContainer.title = "";
|
|
|
menuContainer.id = "betterytm-menu";
|
|
|
+ menuContainer.style.borderRadius = "15px";
|
|
|
|
|
|
|
|
|
// title
|
|
|
const titleCont = document.createElement("div");
|
|
|
+ titleCont.style.padding = "8px 20px 20px 8px";
|
|
|
+ titleCont.style.display = "flex";
|
|
|
+ titleCont.style.justifyContent = "space-between";
|
|
|
titleCont.id = "betterytm-menu-titlecont";
|
|
|
|
|
|
const titleElem = document.createElement("h2");
|
|
|
titleElem.id = "betterytm-menu-title";
|
|
|
- titleElem.innerText = "BetterYTM - Menu";
|
|
|
+ titleElem.innerText = "BetterYTM - Configuration";
|
|
|
|
|
|
const linksCont = document.createElement("div");
|
|
|
linksCont.id = "betterytm-menu-linkscont";
|
|
@@ -208,32 +256,185 @@ function addMenu()
|
|
|
anchorElem.target = "_blank";
|
|
|
anchorElem.href = href;
|
|
|
anchorElem.title = title;
|
|
|
-
|
|
|
- const linkElem = document.createElement("img");
|
|
|
- linkElem.className = "betterytm-menu-img";
|
|
|
- linkElem.src = imgSrc;
|
|
|
-
|
|
|
- anchorElem.appendChild(linkElem);
|
|
|
+ anchorElem.style.marginLeft = "10px";
|
|
|
+
|
|
|
+ const imgElem = document.createElement("img");
|
|
|
+ imgElem.className = "betterytm-menu-img";
|
|
|
+ imgElem.src = imgSrc;
|
|
|
+ imgElem.style.width = "32px";
|
|
|
+ imgElem.style.height = "32px";
|
|
|
+
|
|
|
+ anchorElem.appendChild(imgElem);
|
|
|
linksCont.appendChild(anchorElem);
|
|
|
};
|
|
|
|
|
|
- addLink("TODO:github.png", info.namespace, `${info.name} on GitHub`);
|
|
|
- addLink("TODO:greasyfork.png", "https://greasyfork.org/", `${info.name} on GreasyFork`);
|
|
|
+ addLink(`https://raw.githubusercontent.com/Sv443/BetterYTM/${branch}/resources/external/github.png`, info.namespace, `${info.name} on GitHub`);
|
|
|
+ addLink(`https://raw.githubusercontent.com/Sv443/BetterYTM/${branch}/resources/external/greasyfork.png`, "https://greasyfork.org/xyz", `${info.name} on GreasyFork`);
|
|
|
|
|
|
const closeElem = document.createElement("img");
|
|
|
closeElem.id = "betterytm-menu-close";
|
|
|
- closeElem.src = "TODO:close.png";
|
|
|
+ closeElem.src = `https://raw.githubusercontent.com/Sv443/BetterYTM/${branch}/resources/icon/close.png`;
|
|
|
closeElem.title = "Click to close the menu";
|
|
|
+ closeElem.style.marginLeft = "50px";
|
|
|
+ closeElem.style.width = "32px";
|
|
|
+ closeElem.style.height = "32px";
|
|
|
closeElem.addEventListener("click", closeMenu);
|
|
|
|
|
|
+ linksCont.appendChild(closeElem);
|
|
|
+
|
|
|
titleCont.appendChild(titleElem);
|
|
|
titleCont.appendChild(linksCont);
|
|
|
- titleCont.appendChild(closeElem);
|
|
|
|
|
|
|
|
|
// TODO: features
|
|
|
const featuresCont = document.createElement("div");
|
|
|
featuresCont.id = "betterytm-menu-opts";
|
|
|
+ featuresCont.style.display = "flex";
|
|
|
+ featuresCont.style.flexDirection = "column";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Gets called whenever the feature config is changed
|
|
|
+ * @param {keyof typeof defaultFeatures} key
|
|
|
+ * @param {number|boolean} initialVal
|
|
|
+ * @param {number|boolean} newVal
|
|
|
+ */
|
|
|
+ const confChanged = async (key, initialVal, newVal) => {
|
|
|
+ dbg && console.info(`BetterYTM: Feature config changed, key '${key}' from value '${initialVal}' to '${newVal}'`);
|
|
|
+
|
|
|
+ /** @type {FeatureConfig} */
|
|
|
+ const featConf = {...(await loadFeatureConf())};
|
|
|
+
|
|
|
+ featConf[key] = newVal;
|
|
|
+
|
|
|
+ await saveFeatureConf(featConf);
|
|
|
+
|
|
|
+ dbg && console.log("BetterYTM: Saved feature config changes");
|
|
|
+
|
|
|
+ console.log("#DEBUG", await GM.getValue("bytm-config")); // eslint-disable-line no-undef
|
|
|
+ };
|
|
|
+
|
|
|
+ const featKeys = Object.keys(features);
|
|
|
+ for(const key of featKeys)
|
|
|
+ {
|
|
|
+ const ftInfo = featInfo[key];
|
|
|
+
|
|
|
+ if(!ftInfo)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ const { desc, type, default: ftDef } = ftInfo;
|
|
|
+ const val = features[key];
|
|
|
+
|
|
|
+ const initialVal = val || ftDef;
|
|
|
+
|
|
|
+ const ftConfElem = document.createElement("div");
|
|
|
+ ftConfElem.id = `bytm-ftconf-${key}`;
|
|
|
+ ftConfElem.style.display = "flex";
|
|
|
+ ftConfElem.style.flexDirection = "row";
|
|
|
+ ftConfElem.style.justifyContent = "space-between";
|
|
|
+ ftConfElem.style.padding = "8px 20px";
|
|
|
+
|
|
|
+ {
|
|
|
+ const textElem = document.createElement("span");
|
|
|
+ textElem.style.display = "inline-block";
|
|
|
+ textElem.style.fontSize = "15px";
|
|
|
+ textElem.innerText = desc;
|
|
|
+
|
|
|
+ ftConfElem.appendChild(textElem);
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ let inputType;
|
|
|
+ switch(type)
|
|
|
+ {
|
|
|
+ case "toggle":
|
|
|
+ inputType = "checkbox";
|
|
|
+ break;
|
|
|
+ case "slider":
|
|
|
+ inputType = "range";
|
|
|
+ break;
|
|
|
+ case "number":
|
|
|
+ inputType = "number";
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ const inputElemId = `bytm-ftconf-${key}-input`;
|
|
|
+
|
|
|
+ const ctrlElem = document.createElement("span");
|
|
|
+ ctrlElem.style.display = "inline-block";
|
|
|
+ ctrlElem.style.whiteSpace = "nowrap";
|
|
|
+
|
|
|
+ const inputElem = document.createElement("input");
|
|
|
+ inputElem.id = inputElemId;
|
|
|
+ inputElem.style.marginRight = "20px";
|
|
|
+ inputElem.type = inputType;
|
|
|
+ inputElem.value = initialVal;
|
|
|
+
|
|
|
+ if(ftInfo.min && ftInfo.max)
|
|
|
+ {
|
|
|
+ inputElem.min = ftInfo.min;
|
|
|
+ inputElem.max = ftInfo.max;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(type === "toggle")
|
|
|
+ inputElem.checked = initialVal;
|
|
|
+
|
|
|
+ const fmtVal = v => String(v);
|
|
|
+
|
|
|
+ let labelElem;
|
|
|
+ if(type === "slider")
|
|
|
+ {
|
|
|
+ labelElem = document.createElement("label");
|
|
|
+ labelElem.style.marginRight = "20px";
|
|
|
+ labelElem.style.fontSize = "16px";
|
|
|
+ labelElem["for"] = inputElemId;
|
|
|
+ labelElem.innerText = fmtVal(initialVal);
|
|
|
+
|
|
|
+ inputElem.addEventListener("change", () => labelElem.innerText = fmtVal(parseInt(inputElem.value)));
|
|
|
+ }
|
|
|
+
|
|
|
+ inputElem.addEventListener("change", ({ currentTarget }) => {
|
|
|
+ let v = parseInt(currentTarget.value);
|
|
|
+ if(isNaN(v))
|
|
|
+ v = currentTarget.value;
|
|
|
+ confChanged(key, initialVal, (type !== "toggle" ? v : currentTarget.checked));
|
|
|
+ });
|
|
|
+
|
|
|
+ const resetElem = document.createElement("button");
|
|
|
+ resetElem.innerText = "Reset";
|
|
|
+ resetElem.addEventListener("click", () => {
|
|
|
+ inputElem[type !== "toggle" ? "value" : "checked"] = ftDef;
|
|
|
+
|
|
|
+ if(labelElem)
|
|
|
+ labelElem.innerText = fmtVal(parseInt(inputElem.value));
|
|
|
+
|
|
|
+ confChanged(key, initialVal, ftDef);
|
|
|
+ });
|
|
|
+
|
|
|
+ labelElem && ctrlElem.appendChild(labelElem);
|
|
|
+ ctrlElem.appendChild(inputElem);
|
|
|
+ ctrlElem.appendChild(resetElem);
|
|
|
+
|
|
|
+ ftConfElem.appendChild(ctrlElem);
|
|
|
+ }
|
|
|
+
|
|
|
+ featuresCont.appendChild(ftConfElem);
|
|
|
+ }
|
|
|
+
|
|
|
+ const footerElem = document.createElement("div");
|
|
|
+ footerElem.style.marginTop = "40px";
|
|
|
+ footerElem.style.fontSize = "17px";
|
|
|
+ footerElem.style.textDecoration = "underline";
|
|
|
+ footerElem.style.padding = "8px 20px";
|
|
|
+ footerElem.innerText = "You need to reload the page to apply changes.";
|
|
|
+
|
|
|
+ const reloadElem = document.createElement("button");
|
|
|
+ reloadElem.style.marginLeft = "20px";
|
|
|
+ reloadElem.innerText = "Reload now";
|
|
|
+ reloadElem.title = "Click to reload the page";
|
|
|
+ reloadElem.addEventListener("click", () => location.reload());
|
|
|
+
|
|
|
+ footerElem.appendChild(reloadElem);
|
|
|
+ featuresCont.appendChild(footerElem);
|
|
|
|
|
|
|
|
|
// finalize
|
|
@@ -261,7 +462,7 @@ function addMenu()
|
|
|
display: inline-block;
|
|
|
position: fixed;
|
|
|
width: 50vw;
|
|
|
- height: 50vh;
|
|
|
+ height: auto;
|
|
|
min-height: 500px;
|
|
|
left: 25vw;
|
|
|
top: 25vh;
|
|
@@ -301,7 +502,7 @@ function addMenu()
|
|
|
|
|
|
dbg && console.log("BetterYTM: Added menu elem:", backgroundElem);
|
|
|
|
|
|
- /* #DEBUG */ //openMenu();
|
|
|
+ /* #DEBUG */ openMenu();
|
|
|
|
|
|
addGlobalStyle(menuStyle, "menu");
|
|
|
}
|
|
@@ -314,13 +515,13 @@ function closeMenu()
|
|
|
menuBg.style.display = "none";
|
|
|
}
|
|
|
|
|
|
-// function openMenu()
|
|
|
-// {
|
|
|
-// const menuBg = document.querySelector("#betterytm-menu-bg");
|
|
|
+function openMenu()
|
|
|
+{
|
|
|
+ const menuBg = document.querySelector("#betterytm-menu-bg");
|
|
|
|
|
|
-// menuBg.style.visibility = "visible";
|
|
|
-// menuBg.style.display = "block";
|
|
|
-// }
|
|
|
+ menuBg.style.visibility = "visible";
|
|
|
+ menuBg.style.display = "block";
|
|
|
+}
|
|
|
|
|
|
|
|
|
//#MARKER features
|
|
@@ -495,24 +696,22 @@ function removeUpgradeTab()
|
|
|
function addWatermark()
|
|
|
{
|
|
|
const watermark = document.createElement("a");
|
|
|
-
|
|
|
watermark.id = "betterytm-watermark";
|
|
|
watermark.className = "style-scope ytmusic-nav-bar";
|
|
|
-
|
|
|
watermark.innerText = info.name;
|
|
|
- watermark.title = `${info.name} v${info.version}`;
|
|
|
+ watermark.title = "Open menu";
|
|
|
+ watermark.href = "#";
|
|
|
|
|
|
- watermark.href = info.namespace;
|
|
|
- watermark.target = "_blank";
|
|
|
- watermark.rel = "noopener noreferrer";
|
|
|
+ watermark.addEventListener("click", () => openMenu());
|
|
|
|
|
|
|
|
|
const style = `\
|
|
|
#betterytm-watermark {
|
|
|
+ font-size: 10px;
|
|
|
display: inline-block;
|
|
|
position: absolute;
|
|
|
left: 45px;
|
|
|
- top: 43px;
|
|
|
+ top: 46px;
|
|
|
z-index: 10;
|
|
|
color: white;
|
|
|
text-decoration: none;
|
|
@@ -665,8 +864,6 @@ async function addQueueGeniusBtns()
|
|
|
*/
|
|
|
async function getCurrentGeniusUrl()
|
|
|
{
|
|
|
- return null; //#DEBUG TODO: test how button reacts to API error
|
|
|
-
|
|
|
try
|
|
|
{
|
|
|
// In videos the video title contains both artist and song title, in "regular" YTM songs, the video title only contains the song title
|
|
@@ -758,6 +955,35 @@ async function getGeniusUrl(query)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// #SECTION volume slider
|
|
|
+
|
|
|
+/**
|
|
|
+ * Sets the volume slider to a set size
|
|
|
+ */
|
|
|
+function setVolSliderSize()
|
|
|
+{
|
|
|
+ const { volumeSliderSize: size } = features;
|
|
|
+
|
|
|
+ if(typeof size !== "number" || isNaN(parseInt(size)))
|
|
|
+ return;
|
|
|
+
|
|
|
+const style = `\
|
|
|
+.volume-slider.ytmusic-player-bar, .expand-volume-slider.ytmusic-player-bar {
|
|
|
+ width: ${size}px !important;
|
|
|
+}`;
|
|
|
+
|
|
|
+ addGlobalStyle(style, "vol_slider_size");
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Sets the `step` attribute of the volume slider
|
|
|
+ */
|
|
|
+function setVolSliderStep()
|
|
|
+{
|
|
|
+ const sliderElem = document.querySelector("tp-yt-paper-slider#volume-slider");
|
|
|
+
|
|
|
+ sliderElem.setAttribute("step", features.volumeSliderStep);
|
|
|
+}
|
|
|
|
|
|
//#MARKER other
|
|
|
|
|
@@ -820,14 +1046,17 @@ function insertAfter(beforeNode, afterNode)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Adds global CSS style through a <style> element in the document's <head>
|
|
|
+ * Adds global CSS style through a `<style>` element in the document's `<head>`
|
|
|
* @param {string} style CSS string
|
|
|
- * @param {string} ref Reference name that is included in the <style>'s ID
|
|
|
+ * @param {string} [ref] Reference name that is included in the `<style>`'s ID - defaults to a random number if left undefined
|
|
|
*/
|
|
|
function addGlobalStyle(style, ref)
|
|
|
{
|
|
|
+ if(typeof ref !== "string" || ref.length === 0)
|
|
|
+ ref = String(Math.floor(Math.random() * 1000));
|
|
|
+
|
|
|
const styleElem = document.createElement("style");
|
|
|
- styleElem.id = `betterytm-${ref}-style`;
|
|
|
+ styleElem.id = `bytm-style-${ref}`;
|
|
|
|
|
|
if(styleElem.styleSheet)
|
|
|
styleElem.styleSheet.cssText = style;
|
|
@@ -839,21 +1068,39 @@ function addGlobalStyle(style, ref)
|
|
|
dbg && console.log(`BetterYTM: Inserted global style with ref '${ref}':`, styleElem);
|
|
|
}
|
|
|
|
|
|
+//#SECTION feature config
|
|
|
+
|
|
|
/**
|
|
|
* Loads a feature configuration saved persistently, returns an empty object if no feature configuration was saved
|
|
|
- * @returns {Promise<Readonly<typeof defaultFeatures | {}>>}
|
|
|
+ * @returns {Promise<Readonly<FeatureConfig | {}>>}
|
|
|
*/
|
|
|
async function loadFeatureConf()
|
|
|
{
|
|
|
- /** @type {string} */
|
|
|
- const featureConf = await GM.getValue("bytm-featureconf"); // eslint-disable-line no-undef
|
|
|
+ const defConf = Object.freeze({...defaultFeatures});
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ /** @type {string} */
|
|
|
+ const featureConf = await GM.getValue("bytm-config"); // eslint-disable-line no-undef
|
|
|
+
|
|
|
+ if(!featureConf)
|
|
|
+ {
|
|
|
+ await setDefaultFeatConf();
|
|
|
+ return defConf;
|
|
|
+ }
|
|
|
|
|
|
- return Object.freeze(featureConf ? JSON.parse(featureConf) : {});
|
|
|
+ return Object.freeze(featureConf ? JSON.parse(featureConf) : {});
|
|
|
+ }
|
|
|
+ catch(err)
|
|
|
+ {
|
|
|
+ await setDefaultFeatConf();
|
|
|
+ return defConf;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Saves a feature configuration saved persistently
|
|
|
- * @param {typeof defaultFeatures} featureConf
|
|
|
+ * @param {FeatureConfig} featureConf
|
|
|
* @returns {Promise<void>}
|
|
|
*/
|
|
|
function saveFeatureConf(featureConf)
|
|
@@ -861,7 +1108,15 @@ function saveFeatureConf(featureConf)
|
|
|
if(!featureConf || typeof featureConf != "object")
|
|
|
throw new TypeError("Feature config not provided or invalid");
|
|
|
|
|
|
- return GM.setValue("bytm-featureconf", JSON.stringify(featureConf)); // eslint-disable-line no-undef
|
|
|
+ return GM.setValue("bytm-config", JSON.stringify(featureConf)); // eslint-disable-line no-undef
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @returns {Promise<void>}
|
|
|
+ */
|
|
|
+function setDefaultFeatConf()
|
|
|
+{
|
|
|
+ return GM.setValue("bytm-config", JSON.stringify(defaultFeatures)); // eslint-disable-line no-undef
|
|
|
}
|
|
|
|
|
|
init(); // call init() when script is loaded
|