Ver código fonte

ref: much better logging & stuff

Sv443 1 ano atrás
pai
commit
9482ca8ae6

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/BetterYTM.user.js


+ 11 - 8
src/BetterYTM.user.ts

@@ -1,6 +1,6 @@
 import { getFeatures } from "./config";
-import { dbg, info } from "./constants";
-import { addGlobalStyle, getDomain, initSiteEvents } from "./utils";
+import { logLevel, scriptInfo } from "./constants";
+import { addGlobalStyle, error, getDomain, initSiteEvents, log, setLogLevel } from "./utils";
 import {
   // layout
   initQueueButtons, addWatermark,
@@ -15,7 +15,7 @@ import {
 } from "./features/index";
 
 // TODO: add some style
-console.log(`${info.name} v${info.version} (${info.lastCommit}) - ${info.namespace}`);
+console.log(`${scriptInfo.name} v${scriptInfo.version} (${scriptInfo.lastCommit}) - ${scriptInfo.namespace}`);
 console.log(`Powered by lots of ambition and my song metadata API: ${geniUrlBase}`);
 
 const domain = getDomain();
@@ -34,7 +34,7 @@ async function init() {
     initMenu();
   }
   catch(err) {
-    console.error("BetterYTM: Couldn't initialize menu:", err);
+    error("Couldn't initialize menu:", err);
   }
 }
 
@@ -45,7 +45,7 @@ async function onDomLoad() {
 
   const features = await getFeatures();
 
-  dbg && console.log(`BetterYTM: Initializing features for domain '${domain}'`);
+  log(`Initializing features for domain '${domain}'`);
 
   try {
     if(domain === "ytm") {
@@ -83,16 +83,19 @@ async function onDomLoad() {
         addMenu(); // TODO: remove
       }
       catch(err) {
-        console.error("BetterYTM: Couldn't add menu:", err);
+        console.error("Couldn't add menu:", err);
       }
     }
   }
   catch(err) {
-    console.error("BetterYTM: General error while executing feature:", err);
+    console.error("General error while executing feature:", err);
   }
 }
 
-// needs to be called ASAP, before anything async happens
+// stuff that needs to be called ASAP, before anything async happens
+setLogLevel(logLevel);
+
 if(domain === "ytm")
   initBeforeUnloadHook();
+
 init();

+ 2 - 0
src/config.ts

@@ -1,5 +1,6 @@
 import { featInfo } from "./features/index";
 import { FeatureConfig } from "./types";
+import { log } from "./utils";
 
 export const defaultFeatures = (Object.keys(featInfo) as (keyof typeof featInfo)[]).reduce<Partial<FeatureConfig>>((acc, key) => {
   acc[key] = featInfo[key].default as unknown as undefined;
@@ -47,6 +48,7 @@ export function saveFeatureConf(featureConf: FeatureConfig) {
   if(!featureConf || typeof featureConf != "object")
     throw new TypeError("Feature config not provided or invalid");
 
+  log("");
   featuresCache = { ...featureConf };
   return GM.setValue("betterytm-config", JSON.stringify(featureConf));
 }

+ 11 - 3
src/constants.ts

@@ -1,13 +1,21 @@
-/** Set to true to enable debug mode for more output in the JS console */
-export const dbg = true;
+import { LogLevel } from "./types";
 
+/** The branch to use in the @icon, @downloadURL and @updateURL directives */
+export const branch = "develop";
+// export const branch = "main";
+
+/**
+ * How much info should be logged to the devtools console?  
+ * 0 = Debug (show everything) or 1 = Info (show only important stuff)
+ */
+export const logLevel: LogLevel = 0;
 /** Specifies the hard limit for repetitive tasks */
 export const triesLimit = 25;
 /** Specifies the interval for repetitive tasks */
 export const triesInterval = 200;
 
 /** Info about the userscript, parsed from the userscript header (tools/post-build.js) */
-export const info = Object.freeze({
+export const scriptInfo = Object.freeze({
   name: GM.info.script.name,
   version: GM.info.script.version,
   namespace: GM.info.script.namespace,

+ 2 - 2
src/features/index.ts

@@ -1,4 +1,4 @@
-import { info } from "../constants";
+import { scriptInfo } from "../constants";
 
 export * from "./input";
 export * from "./layout";
@@ -73,7 +73,7 @@ export const featInfo = Object.freeze({
     default: 2,
   },
   watermarkEnabled: {
-    desc: `Show a ${info.name} watermark under the YTM logo`,
+    desc: `Show a ${scriptInfo.name} watermark under the YTM logo`,
     type: "toggle",
     category: "layout",
     default: true,

+ 12 - 13
src/features/input.ts

@@ -1,5 +1,4 @@
-import { getVideoTime } from "../utils";
-import { dbg } from "../constants";
+import { getVideoTime, info, log, warn } from "../utils";
 import type { Domain } from "../types";
 import { getFeatures } from "../config";
 
@@ -7,7 +6,7 @@ import { getFeatures } from "../config";
 
 export function initArrowKeySkip() {
   document.addEventListener("keydown", onKeyDown);
-  dbg && console.log("BetterYTM: Added key press listener");
+  log("Added key press listener");
 }
 
 /** Called when the user presses any key, anywhere */
@@ -15,9 +14,9 @@ function onKeyDown(evt: KeyboardEvent) {
   if(["ArrowLeft", "ArrowRight"].includes(evt.code)) {
     // discard the event when a (text) input is currently active, like when editing a playlist
     if(["INPUT", "TEXTAREA", "SELECT"].includes(document.activeElement?.tagName ?? "_"))
-      return dbg && console.info(`BetterYTM: Captured valid key but the current active element is <${document.activeElement!.tagName.toLowerCase()}>, so the keypress is ignored`);
+      return info(`Captured valid key but the current active element is <${document.activeElement!.tagName.toLowerCase()}>, so the keypress is ignored`);
 
-    dbg && console.log(`BetterYTM: Captured key '${evt.code}' in proxy listener`);
+    log(`Captured key '${evt.code}' in proxy listener`);
 
     // ripped this stuff from the console, most of these are probably unnecessary but this was finnicky af and I am sick and tired of trial and error
     const defaultProps = {
@@ -70,10 +69,10 @@ function onKeyDown(evt: KeyboardEvent) {
 
       document.body.dispatchEvent(new KeyboardEvent("keydown", proxyProps));
 
-      dbg && console.log(`BetterYTM: Dispatched proxy keydown event: [${evt.code}] -> [${proxyProps.code}]`);
+      log(`Dispatched proxy keydown event: [${evt.code}] -> [${proxyProps.code}]`);
     }
-    else if(dbg)
-      console.warn(`BetterYTM: Captured key '${evt.code}' has no defined behavior`);
+    else
+      warn(`Captured key '${evt.code}' has no defined behavior`);
   }
 }
 
@@ -90,7 +89,7 @@ export function initSiteSwitch(domain: Domain) {
     if(e.key === "F9")
       switchSite(domain === "yt" ? "ytm" : "yt");
   });
-  dbg && console.log("BetterYTM: Initialized site switch listener");
+  log("Initialized site switch listener");
 }
 
 /** Switches to the other site (between YT and YTM) */
@@ -110,7 +109,7 @@ function switchSite(newDomain: Domain) {
 
     const vt = getVideoTime() ?? 0;
 
-    dbg && console.log(`BetterYTM: Found video time of ${vt} seconds`);
+    log(`Found video time of ${vt} seconds`);
 
     const newSearch = search.includes("?") ? `${search}&t=${vt}` : `?t=${vt}`;
 
@@ -122,7 +121,7 @@ function switchSite(newDomain: Domain) {
     setImmediate(() => location.href = url);
   }
   catch(err) {
-    console.error("BetterYTM: Error while switching site:", err);
+    console.error("Error while switching site:", err);
   }
 }
 
@@ -133,13 +132,13 @@ let beforeUnloadEnabled = true;
 /** Disables the popup before leaving the site */
 export function disableBeforeUnload() {
   beforeUnloadEnabled = false;
-  dbg && console.info("BetterYTM: Disabled popup before leaving the site");
+  info("Disabled popup before leaving the site");
 }
 
 /** (Re-)enables the popup before leaving the site */
 export function enableBeforeUnload() {
   beforeUnloadEnabled = true;
-  dbg && console.info("BetterYTM: Enabled popup before leaving the site");
+  info("Enabled popup before leaving the site");
 }
 
 /** Adds a spy function into `window.__proto__.addEventListener` to selectively discard events before they can be captured by the original site's listeners */

+ 6 - 6
src/features/layout.ts

@@ -1,6 +1,6 @@
-import { dbg, info, triesInterval, triesLimit } from "../constants";
+import { scriptInfo, triesInterval, triesLimit } from "../constants";
 import { getFeatures } from "../config";
-import { addGlobalStyle, insertAfter, siteEvents } from "../utils";
+import { addGlobalStyle, insertAfter, log, siteEvents } from "../utils";
 import type { FeatureConfig } from "../types";
 import { openMenu } from "./menu/menu_old";
 import "./layout.css";
@@ -18,7 +18,7 @@ export function addWatermark() {
   const watermark = document.createElement("span");
   watermark.id = "betterytm-watermark";
   watermark.className = "style-scope ytmusic-nav-bar";
-  watermark.innerText = info.name;
+  watermark.innerText = scriptInfo.name;
   watermark.title = "Open menu";
 
   watermark.addEventListener("click", () => openMenu());
@@ -26,7 +26,7 @@ export function addWatermark() {
   const logoElem = document.querySelector("#left-content") as HTMLElement;
   insertAfter(logoElem, watermark);
 
-  dbg && console.log("BetterYTM: Added watermark element:", watermark);
+  log("Added watermark element:", watermark);
 }
 
 //#MARKER remove upgrade tab
@@ -38,14 +38,14 @@ export function removeUpgradeTab() {
   const tabElem = document.querySelector(".ytmusic-nav-bar ytmusic-pivot-bar-item-renderer[tab-id=\"SPunlimited\"]");
   if(tabElem) {
     tabElem.remove();
-    dbg && console.log(`BetterYTM: Removed upgrade tab after ${removeUpgradeTries} tries`);
+    log(`Removed upgrade tab after ${removeUpgradeTries} tries`);
   }
   else if(removeUpgradeTries < triesLimit) {
     setTimeout(removeUpgradeTab, triesInterval); // TODO: improve this
     removeUpgradeTries++;
   }
   else
-    console.error(`BetterYTM: Couldn't find upgrade tab to remove after ${removeUpgradeTries} tries`);
+    console.error(`Couldn't find upgrade tab to remove after ${removeUpgradeTries} tries`);
 }
 
 //#MARKER volume slider

+ 10 - 10
src/features/lyrics.ts

@@ -1,5 +1,5 @@
-import { dbg, triesInterval, triesLimit } from "../constants";
-import { insertAfter } from "../utils";
+import { triesInterval, triesLimit } from "../constants";
+import { error, info, insertAfter, log } from "../utils";
 import "./lyrics.css";
 
 /** Base URL of geniURL */
@@ -19,7 +19,7 @@ export async function addMediaCtrlGeniusBtn(): Promise<unknown> {
     if(mcLyricsButtonAddTries < triesLimit)
       return setTimeout(addMediaCtrlGeniusBtn, triesInterval); // TODO: improve this
 
-    return console.error(`BetterYTM: Couldn't find element to append lyrics buttons to after ${mcLyricsButtonAddTries} tries`);
+    return console.error(`Couldn't find element to append lyrics buttons to after ${mcLyricsButtonAddTries} tries`);
   }
 
   const songTitleElem = document.querySelector(".content-info-wrapper > yt-formatted-string") as HTMLDivElement;
@@ -45,7 +45,7 @@ export async function addMediaCtrlGeniusBtn(): Promise<unknown> {
 
   linkElem.appendChild(imgElem);
 
-  dbg && console.log(`BetterYTM: Inserted genius button after ${mcLyricsButtonAddTries} tries:`, linkElem);
+  log(`Inserted genius button after ${mcLyricsButtonAddTries} tries:`, linkElem);
 
   insertAfter(likeContainer, linkElem);
 
@@ -62,7 +62,7 @@ export async function addMediaCtrlGeniusBtn(): Promise<unknown> {
         if(!lyricsBtn)
           return;
 
-        dbg && console.log(`BetterYTM: Song title changed from '${mcCurrentSongTitle}' to '${newTitle}'`);
+        log(`Song title changed from '${mcCurrentSongTitle}' to '${newTitle}'`);
 
         lyricsBtn.style.cursor = "wait";
         lyricsBtn.style.pointerEvents = "none";
@@ -149,7 +149,7 @@ export async function getCurrentGeniusUrl() {
   }
   catch(err)
   {
-    console.error("BetterYTM: Couldn't resolve genius.com URL:", err);
+    error("Couldn't resolve genius.com URL:", err);
     return null;
   }
 }
@@ -162,24 +162,24 @@ async function getGeniusUrl(artist: string, song: string): Promise<string | unde
   try {
     const fetchUrl = `${geniURLSearchTopUrl}?artist=${encodeURIComponent(artist)}&song=${encodeURIComponent(song)}`;
 
-    dbg && console.log(`BetterYTM: Requesting URL from geniURL at '${fetchUrl}'`);
+    log(`Requesting URL from geniURL at '${fetchUrl}'`);
 
     const result = await (await fetch(fetchUrl)).json();
 
     if(result.error)
     {
-      console.error("BetterYTM: Couldn't fetch genius.com URL:", result.message);
+      error("Couldn't fetch genius.com URL:", result.message);
       return undefined;
     }
 
     const url = result.url;
 
-    dbg && console.info(`BetterYTM: Found genius URL: ${url}`);
+    info(`Found genius URL: ${url}`);
 
     return url;
   }
   catch(err) {
-    console.error("BetterYTM: Couldn't get genius URL due to error:", err);
+    error("Couldn't get genius URL due to error:", err);
     return undefined;
   }
 }

+ 1 - 1
src/features/menu/menu.ts

@@ -36,7 +36,7 @@ function initMenuContents(): unknown {
   if(!document.querySelector("#bytm-menu-dialog"))
     return menuContTries++ < triesLimit
       ? setTimeout(initMenuContents, triesInterval)
-      : console.error(`BetterYTM: couldn't create menu element after ${triesLimit} tries.`);
+      : console.error(`couldn't create menu element after ${triesLimit} tries.`);
 
   // hook events
   for(const tab in tabsSelectors) {

+ 9 - 11
src/features/menu/menu_old.ts

@@ -1,10 +1,8 @@
 import { defaultFeatures, getFeatures, saveFeatureConf } from "../../config";
-import { dbg, info } from "../../constants";
+import { branch, scriptInfo } from "../../constants";
 import { featInfo } from "../index";
 import { FeatureConfig } from "../../types";
-import { addGlobalStyle } from "../../utils";
-
-const branch = dbg ? "develop" : "main";
+import { addGlobalStyle, info, log } from "../../utils";
 
 //#MARKER menu
 
@@ -42,7 +40,7 @@ export async function addMenu() {
 
   const titleElem = document.createElement("h2");
   titleElem.id = "betterytm-menu-title";
-  titleElem.innerText = `${info.name} - Configuration`;
+  titleElem.innerText = `${scriptInfo.name} - Configuration`;
 
   const linksCont = document.createElement("div");
   linksCont.id = "betterytm-menu-linkscont";
@@ -66,8 +64,8 @@ export async function addMenu() {
     linksCont.appendChild(anchorElem);
   };
 
-  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`);
+  addLink(`https://raw.githubusercontent.com/Sv443/BetterYTM/${branch}/resources/external/github.png`, scriptInfo.namespace, `${scriptInfo.name} on GitHub`);
+  addLink(`https://raw.githubusercontent.com/Sv443/BetterYTM/${branch}/resources/external/greasyfork.png`, "https://greasyfork.org/xyz", `${scriptInfo.name} on GreasyFork`);
 
   const closeElem = document.createElement("img");
   closeElem.id = "betterytm-menu-close";
@@ -94,7 +92,7 @@ export async function addMenu() {
   /** Gets called whenever the feature config is changed */
   const confChanged = async (key: keyof typeof defaultFeatures, initialVal: number | boolean | Record<string, unknown>, newVal: number | boolean | Record<string, unknown>) => {
     const fmt = (val: unknown) => typeof val === "object" ? JSON.stringify(val) : String(val);
-    dbg && console.info(`BetterYTM: Feature config changed, key '${key}' from value '${fmt(initialVal)}' to '${fmt(newVal)}'`);
+    info(`Feature config changed, key '${key}' from value '${fmt(initialVal)}' to '${fmt(newVal)}'`);
 
     const featConf = { ...await getFeatures() };
 
@@ -102,7 +100,7 @@ export async function addMenu() {
 
     await saveFeatureConf(featConf);
 
-    dbg && console.log("BetterYTM: Saved feature config changes:\n", await GM.getValue("betterytm-config"));
+    log("Saved feature config changes:\n", await GM.getValue("betterytm-config"));
   };
 
   const features = await getFeatures();
@@ -285,7 +283,7 @@ export async function addMenu() {
 
   const versionElem = document.createElement("span");
   versionElem.id = "betterytm-menu-version";
-  versionElem.innerText = `v${info.version}`;
+  versionElem.innerText = `v${scriptInfo.version}`;
 
   versionCont.appendChild(versionElem);
   featuresCont.appendChild(versionCont);
@@ -364,7 +362,7 @@ export async function addMenu() {
   user-select: none;
 }`;
 
-  dbg && console.log("BetterYTM: Added menu elem:", backgroundElem);
+  log("Added menu elem:", backgroundElem);
 
   addGlobalStyle(menuStyle, "menu");
 }

+ 5 - 4
src/tools/serve.ts

@@ -12,10 +12,11 @@ const ringBell = true;
 
 const app = express();
 
-app.use((_req, _res, next) => {
-  enableLogging && process.stdout.write("*");
-  next();
-});
+enableLogging &&
+  app.use((_req, _res, next) => {
+    process.stdout.write("*");
+    next();
+  });
 
 app.use((err: unknown, _req: Request, _res: Response, _next: NextFunction) => {
   if(typeof err === "string" || err instanceof Error)

+ 3 - 0
src/types.d.ts

@@ -1,3 +1,6 @@
+/** 0 = Debug, 1 = Info */
+export type LogLevel = 0 | 1;
+
 /** Which domain this script is currently running on */
 export type Domain = "yt" | "ytm";
 

+ 73 - 21
src/utils.ts

@@ -1,9 +1,54 @@
 import { EventEmitter, EventHandler } from "@billjs/event-emitter";
-import { dbg } from "./constants";
-import type { Domain } from "./types";
+import type { Domain, LogLevel } from "./types";
+import { scriptInfo } from "./constants";
 
 //#SECTION BYTM-specific
 
+let curLogLevel: LogLevel = 1;
+
+/** Sets the current log level. 0 = Debug, 1 = Info */
+export function setLogLevel(level: LogLevel) {
+  curLogLevel = level;
+}
+
+function getLogLevel(...args: unknown[]): number {
+  if(typeof args.at(-1) === "number")
+    return args.splice(args.length - 1, 1)[0] as number;
+  return 0;
+}
+
+/**
+ * Logs string-compatible values to the console, as long as the log level is sufficient.  
+ * @param args Last parameter is logLevel: 0 = Debug, 1/undefined = Info
+ */
+export function log(...args: unknown[]): void {
+  if(curLogLevel <= getLogLevel(...args))
+    console.log(`${scriptInfo.name}: `, ...args);
+}
+
+/**
+ * Logs string-compatible values to the console as info, as long as the log level is sufficient.  
+ * @param args Last parameter is logLevel: 0 = Debug, 1/undefined = Info
+ */
+export function info(...args: unknown[]): void {
+  if(curLogLevel <= getLogLevel(...args))
+    console.info(`${scriptInfo.name}: `, ...args);
+}
+
+/**
+ * Logs string-compatible values to the console as a warning, as long as the log level is sufficient.  
+ * @param args Last parameter is logLevel: 0 = Debug, 1/undefined = Info
+ */
+export function warn(...args: unknown[]): void {
+  if(curLogLevel <= getLogLevel(...args))
+    console.warn(`${scriptInfo.name}: `, ...args);
+}
+
+/** Logs string-compatible values to the console as an error. */
+export function error(...args: unknown[]): void {
+  console.error(`${scriptInfo.name}: `, ...args);
+}
+
 /**
  * Returns the current domain as a constant string representation
  * @throws Throws if script runs on an unexpected website
@@ -55,7 +100,7 @@ export function getVideoTime(force = false) {
     return null;
   }
   catch(err) {
-    console.error("BetterYTM: Couldn't get video time due to error:", err);
+    console.error("Couldn't get video time due to error:", err);
     return null;
   }
 }
@@ -124,7 +169,7 @@ export function addGlobalStyle(style: string, ref?: string) {
   styleElem.innerHTML = style;
   document.head.appendChild(styleElem);
 
-  dbg && console.log(`BetterYTM: Inserted global style with ref '${ref}':`, styleElem);
+  log(`Inserted global style with ref '${ref}':`, styleElem);
 }
 
 //#SECTION site events
@@ -138,26 +183,33 @@ export const siteEvents = new EventEmitter() as SiteEvents;
 
 let observers: MutationObserver[] = [];
 
+/** Creates MutationObservers that check if parts of the site have changed, then emit an event on the `siteEvents` instance */
 export function initSiteEvents() {
-  const queueObserver = new MutationObserver(([ { addedNodes, removedNodes, target } ]) => {
-    if(addedNodes.length > 0 || removedNodes.length > 0) {
-      dbg && console.info("BetterYTM: Detected queue change - added nodes:", addedNodes.length, "- removed nodes:", removedNodes.length);
-      siteEvents.fire("queueChanged", target);
-    }
-  });
-  // only observe added or removed elements
-  queueObserver.observe(document.querySelector(".side-panel.modular #contents.ytmusic-player-queue")!, {
-    childList: true,
-  });
-
-  dbg && console.info("BetterYTM: Successfully initialized SiteEvents observers");
-
-  observers = [
-    queueObserver,
-  ];
+  try {
+    const queueObserver = new MutationObserver(([ { addedNodes, removedNodes, target } ]) => {
+      if(addedNodes.length > 0 || removedNodes.length > 0) {
+        info("Detected queue change - added nodes:", addedNodes.length, "- removed nodes:", removedNodes.length);
+        siteEvents.fire("queueChanged", target);
+      }
+    });
+    // only observe added or removed elements
+    queueObserver.observe(document.querySelector(".side-panel.modular #contents.ytmusic-player-queue")!, {
+      childList: true,
+    });
+
+    info("Successfully initialized SiteEvents observers");
+
+    observers = [
+      queueObserver,
+    ];
+  }
+  catch(err) {
+    console.error("Couldn't initialize SiteEvents observers due to an error:\n", err);
+  }
 }
 
-export function removeObservers() {
+/** Disconnects and deletes all observers. Run `initSiteEvents()` again to create new ones. */
+export function removeAllObservers() {
   observers.forEach((observer) => observer.disconnect());
   observers = [];
 }

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff