Ver código fonte

ref: use @resource instead of fetching from GH

Sv443 1 ano atrás
pai
commit
06a461072e

+ 10 - 0
assets/resources.json

@@ -0,0 +1,10 @@
+{
+  "icon": "icon/icon.png",
+  "close": "close.png",
+  "delete": "delete.svg",
+  "error": "error.svg",
+  "lyrics": "lyrics.svg",
+  "spinner": "spinner.svg",
+  "github": "external/github.png",
+  "greasyfork": "external/greasyfork.png"
+}

+ 17 - 11
src/features/layout.ts

@@ -3,7 +3,7 @@ import { addGlobalStyle, addParent, autoPlural, fetchAdvanced, insertAfter, onSe
 import type { FeatureConfig } from "../types";
 import { scriptInfo } from "../constants";
 import { getFeatures } from "../config";
-import { error, getAssetUrl, log } from "../utils";
+import { error, getResourceUrl, log } from "../utils";
 import { getEvtData, siteEvents } from "../events";
 import { openMenu } from "./menu/menu_old";
 import { getGeniusUrl, createLyricsBtn, sanitizeArtists, sanitizeSong, getLyricsCacheEntry } from "./lyrics";
@@ -90,21 +90,23 @@ async function improveLogo() {
 /** Exchanges the default YTM logo into BetterYTM's logo with a sick ass animation */
 function exchangeLogo() {
   onSelector(".bytm-mod-logo", {
-    listener: (logoElem) => {
+    listener: async (logoElem) => {
       if(logoElem.classList.contains("bytm-logo-exchanged"))
         return;
 
       logoExchanged = true;
       logoElem.classList.add("bytm-logo-exchanged");
 
+      const iconUrl = await getResourceUrl("icon");
+
       const newLogo = document.createElement("img");
       newLogo.className = "bytm-mod-logo-img";
-      newLogo.src = getAssetUrl("icon/icon.png");
+      newLogo.src = iconUrl;
 
       logoElem.insertBefore(newLogo, logoElem.querySelector("svg"));
 
-      document.head.querySelectorAll<HTMLLinkElement>("link[rel=\"icon\"]").forEach(e => {
-        e.href = getAssetUrl("icon/icon.png");
+      document.head.querySelectorAll<HTMLLinkElement>("link[rel=\"icon\"]").forEach((e) => {
+        e.href = iconUrl;
       });
 
       setTimeout(() => {
@@ -115,7 +117,7 @@ function exchangeLogo() {
 }
 
 /** Called whenever the menu exists to add a BYTM-Configuration button */
-export function addConfigMenuOption(container: HTMLElement) {
+export async function addConfigMenuOption(container: HTMLElement) {
   const cfgOptElem = document.createElement("a");
   cfgOptElem.role = "button";
   cfgOptElem.className = "bytm-cfg-menu-option bytm-anchor";
@@ -135,7 +137,7 @@ export function addConfigMenuOption(container: HTMLElement) {
 
   const cfgOptIconElem = document.createElement("img");
   cfgOptIconElem.className = "bytm-cfg-menu-option-icon";
-  cfgOptIconElem.src = getAssetUrl("icon/icon.png");
+  cfgOptIconElem.src = await getResourceUrl("icon");
 
   const cfgOptTextElem = document.createElement("div");
   cfgOptTextElem.className = "bytm-cfg-menu-option-text";
@@ -295,8 +297,12 @@ async function addQueueButtons(queueItem: HTMLElement) {
   if(!song || !artist)
     return false;
 
+
+  const lyricsIconUrl = await getResourceUrl("lyrics");
+  const deleteIconUrl = await getResourceUrl("delete");
+
   //#SECTION lyrics btn
-  const lyricsBtnElem = createLyricsBtn(undefined, false);
+  const lyricsBtnElem = await createLyricsBtn(undefined, false);
   {
     lyricsBtnElem.title = "Open this song's lyrics in a new tab";
     lyricsBtnElem.style.display = "inline-flex";
@@ -318,14 +324,14 @@ async function addQueueButtons(queueItem: HTMLElement) {
         if(!cachedLyricsUrl) {
           songInfo.setAttribute("data-bytm-loading", "");
 
-          imgEl.src = getAssetUrl("spinner.svg");
+          imgEl.src = await getResourceUrl("spinner");
           imgEl.classList.add("bytm-spinner");
         }
 
         lyricsUrl = cachedLyricsUrl ?? await getGeniusUrl(artistsSan, songSan);
 
         const resetImgElem = () => {
-          imgEl.src = getAssetUrl("lyrics.svg");
+          imgEl.src = lyricsIconUrl;
           imgEl.classList.remove("bytm-spinner");
         };
 
@@ -394,7 +400,7 @@ async function addQueueButtons(queueItem: HTMLElement) {
 
     const imgElem = document.createElement("img");
     imgElem.className = "bytm-generic-btn-img";
-    imgElem.src = getAssetUrl("delete.svg");
+    imgElem.src = deleteIconUrl;
 
     deleteBtnElem.appendChild(imgElem);
   }

+ 10 - 7
src/features/lyrics.ts

@@ -1,5 +1,5 @@
 import { clamp, fetchAdvanced, insertAfter, onSelector } from "@sv443-network/userutils";
-import { error, getAssetUrl, info, log } from "../utils";
+import { error, getResourceUrl, info, log } from "../utils";
 
 /** Base URL of geniURL */
 export const geniUrlBase = "https://api.sv443.net/geniurl";
@@ -50,14 +50,14 @@ export function addMediaCtrlLyricsBtn(): void {
 
 // TODO: add error.svg if the request fails
 /** Actually adds the lyrics button after the like button renderer has been verified to exist */
-function addActualMediaCtrlLyricsBtn(likeContainer: HTMLElement) {
+async function addActualMediaCtrlLyricsBtn(likeContainer: HTMLElement) {
   const songTitleElem = document.querySelector(".content-info-wrapper > yt-formatted-string") as HTMLDivElement;
 
   // run parallel without awaiting so the MutationObserver below can observe the title element in time
   (async () => {
     const gUrl = await getCurrentLyricsUrl();
 
-    const linkElem = createLyricsBtn(gUrl ?? undefined);
+    const linkElem = await createLyricsBtn(gUrl ?? undefined);
     linkElem.id = "betterytm-lyrics-button";
 
     log("Inserted lyrics button into media controls bar");
@@ -67,6 +67,9 @@ function addActualMediaCtrlLyricsBtn(likeContainer: HTMLElement) {
 
   mcCurrentSongTitle = songTitleElem.title;
 
+  const spinnerIconUrl = await getResourceUrl("spinner");
+  const lyricsIconUrl = await getResourceUrl("lyrics");
+
   const onMutation = async (mutations: MutationRecord[]) => {
     for await(const mut of mutations) {
       const newTitle = (mut.target as HTMLElement).title;
@@ -83,14 +86,14 @@ function addActualMediaCtrlLyricsBtn(likeContainer: HTMLElement) {
         lyricsBtn.style.pointerEvents = "none";
 
         const imgElem = lyricsBtn.querySelector<HTMLImageElement>("img")!;
-        imgElem.src = getAssetUrl("spinner.svg");
+        imgElem.src = spinnerIconUrl;
         imgElem.classList.add("bytm-spinner");
 
         mcCurrentSongTitle = newTitle;
 
         const url = await getCurrentLyricsUrl(); // can take a second or two
 
-        imgElem.src = getAssetUrl("lyrics.svg");
+        imgElem.src = lyricsIconUrl;
         imgElem.classList.remove("bytm-spinner");
 
         if(!url)
@@ -224,7 +227,7 @@ export async function getGeniusUrl(artist: string, song: string): Promise<string
 }
 
 /** Creates the base lyrics button element */
-export function createLyricsBtn(geniusUrl?: string, hideIfLoading = true): HTMLAnchorElement {
+export async function createLyricsBtn(geniusUrl?: string, hideIfLoading = true) {
   const linkElem = document.createElement("a");
   linkElem.className = "ytmusic-player-bar bytm-generic-btn";
   linkElem.title = geniusUrl ? "Click to open this song's lyrics in a new tab" : "Loading lyrics URL...";
@@ -238,7 +241,7 @@ export function createLyricsBtn(geniusUrl?: string, hideIfLoading = true): HTMLA
 
   const imgElem = document.createElement("img");
   imgElem.className = "bytm-generic-btn-img";
-  imgElem.src = getAssetUrl("lyrics.svg");
+  imgElem.src = await getResourceUrl("lyrics");
 
   linkElem.appendChild(imgElem);
 

+ 4 - 4
src/features/menu/menu_old.ts

@@ -3,7 +3,7 @@ import { defaultFeatures, getFeatures, saveFeatureConf, setDefaultFeatConf } fro
 import { scriptInfo } from "../../constants";
 import { featInfo } from "../index";
 import { FeatureConfig } from "../../types";
-import { getAssetUrl, info, log } from "../../utils";
+import { getResourceUrl, info, log } from "../../utils";
 import "./menu_old.css";
 
 //#MARKER create menu elements
@@ -73,12 +73,12 @@ export async function addMenu() {
     linksCont.appendChild(anchorElem);
   };
 
-  addLink(getAssetUrl("external/github.png"), scriptInfo.namespace, `${scriptInfo.name} on GitHub`);
-  addLink(getAssetUrl("external/greasyfork.png"), "https://greasyfork.org/xyz", `${scriptInfo.name} on GreasyFork`);
+  addLink(await getResourceUrl("github"), scriptInfo.namespace, `${scriptInfo.name} on GitHub`);
+  addLink(await getResourceUrl("greasyfork"), "https://greasyfork.org/xyz", `${scriptInfo.name} on GreasyFork`);
 
   const closeElem = document.createElement("img");
   closeElem.id = "betterytm-menu-close";
-  closeElem.src = getAssetUrl("close.png");
+  closeElem.src = await getResourceUrl("close");
   closeElem.title = "Click to close the menu";
   closeElem.style.marginLeft = "50px";
   closeElem.style.width = "32px";

+ 4 - 22
src/index.ts

@@ -1,7 +1,7 @@
-import { addGlobalStyle, autoPlural, preloadImages, initOnSelector, onSelector, getSelectorMap } from "@sv443-network/userutils";
+import { addGlobalStyle, initOnSelector, onSelector } from "@sv443-network/userutils";
 import { loadFeatureConf } from "./config";
 import { logLevel, scriptInfo } from "./constants";
-import { dbg, error, getAssetUrl, getDomain, log, setLogLevel } from "./utils";
+import { error, getDomain, log, setLogLevel } from "./utils";
 import { initSiteEvents } from "./events";
 import {
   // layout
@@ -16,14 +16,6 @@ import {
   initMenu, addMenu, initBeforeUnloadHook, addConfigMenuOption,
 } from "./features/index";
 
-/** URLs of images to pre-cache so they can be displayed instantly */
-const preloadImgs = [
-  getAssetUrl("icon/icon.png"),
-  getAssetUrl("spinner.svg"),
-  getAssetUrl("delete.svg"),
-  getAssetUrl("lyrics.svg"),
-];
-
 {
   // console watermark with sexy gradient
   const styleGradient = "background: rgba(165, 38, 38, 1); background: linear-gradient(90deg, rgb(154, 31, 103) 0%, rgb(135, 31, 31) 40%, rgb(184, 64, 41) 100%);";
@@ -59,11 +51,6 @@ function preInit() {
 }
 
 async function init() {
-  if(domain === "ytm")
-    preloadImages(preloadImgs, true)
-      .then(() => log(`Preloaded ${preloadImgs.length} ${autoPlural("image", preloadImgs)}`))
-      .catch((e) => error(`Couldn't preload images: ${e}`));
-
   await preInitLayout();
 
   try {
@@ -124,13 +111,8 @@ async function onDomLoad() {
       if(features.anchorImprovements)
         addAnchorImprovements();
 
-      //#DEBUG
-      setInterval(() => {
-        const selMap = getSelectorMap();
-        dbg("selector map", selMap.size, selMap);
-      }, 500);
-
-      // initVolumeFeatures();
+      // TODO:
+      void initVolumeFeatures;
     }
 
     if(["ytm", "yt"].includes(domain)) {

+ 60 - 37
src/tools/post-build.ts

@@ -22,48 +22,12 @@ const devServerPort = isNaN(envPort) || envPort === 0 ? 8710 : envPort;
 const repo = "Sv443/BetterYTM";
 const userscriptDistFile = `BetterYTM${outFileSuffix}.user.js`;
 const distFolderPath = "./dist/";
+const assetFolderPath = "./assets/";
 const scriptUrl = `https://raw.githubusercontent.com/${repo}/${branch}/dist/${userscriptDistFile}`;
 
 /** Whether to trigger the bell sound in some terminals when the code has finished compiling */
 const ringBell = Boolean(env.RING_BELL && (env.RING_BELL.length > 0 && env.RING_BELL.trim().toLowerCase() === "true"));
 
-const header = `\
-// ==UserScript==
-// @name            ${pkg.userscriptName}
-// @homepageURL     ${pkg.homepage}#readme
-// @namespace       ${pkg.homepage}
-// @version         ${pkg.version}
-// @description     ${pkg.description}
-// @description:de  ${pkg["description:de"]}
-// @license         ${pkg.license}
-// @author          ${pkg.author.name}
-// @copyright       ${pkg.author.name} (${pkg.author.url})
-// @icon            https://raw.githubusercontent.com/${repo}/${branch}/assets/icon/icon.png
-// @match           https://music.youtube.com/*
-// @match           https://www.youtube.com/*
-// @run-at          document-start
-// @downloadURL     ${scriptUrl}
-// @updateURL       ${scriptUrl}
-// @connect         api.sv443.net
-// @grant           GM.getValue
-// @grant           GM.setValue
-// @grant           unsafeWindow
-// ==/UserScript==
-/*
- ▄▄▄                    ▄   ▄▄▄▄▄▄   ▄
- █  █ ▄▄▄ █   █   ▄▄▄ ▄ ▄█ █  █  █▀▄▀█
- █▀▀▄ █▄█ █▀  █▀  █▄█ █▀  █   █  █   █
- █▄▄▀ ▀▄▄ ▀▄▄ ▀▄▄ ▀▄▄ █   █   █  █   █
-
-         Made with ❤️ by Sv443
- I welcome every contribution on GitHub!
-   https://github.com/Sv443/BetterYTM
-*/
-
-/* Disclaimer: I am not affiliated with YouTube, Google, Alphabet, Genius or anyone else */
-/* C&D this 🖕 */
-`;
-
 type BuildStats = {
   sizeKiB: number;
   mode: string;
@@ -71,6 +35,48 @@ type BuildStats = {
 };
 
 (async () => {
+  const resourcesDirectives = await getResourceDirectives();
+
+  const header = `\
+  // ==UserScript==
+  // @name            ${pkg.userscriptName}
+  // @homepageURL     ${pkg.homepage}#readme
+  // @namespace       ${pkg.homepage}
+  // @version         ${pkg.version}
+  // @description     ${pkg.description}
+  // @description:de  ${pkg["description:de"]}
+  // @license         ${pkg.license}
+  // @author          ${pkg.author.name}
+  // @copyright       ${pkg.author.name} (${pkg.author.url})
+  // @icon            https://raw.githubusercontent.com/${repo}/${branch}/assets/icon/icon.png
+  // @match           https://music.youtube.com/*
+  // @match           https://www.youtube.com/*
+  // @run-at          document-start
+  // @downloadURL     ${scriptUrl}
+  // @updateURL       ${scriptUrl}
+  // @connect         api.sv443.net
+  // @grant           GM.getValue
+  // @grant           GM.setValue
+  // @grant           GM.getResourceUrl
+  // @grant           unsafeWindow
+  // @noframes\
+  ${resourcesDirectives ? "\n" + resourcesDirectives : ""}
+  // ==/UserScript==
+  /*
+  ▄▄▄                    ▄   ▄▄▄▄▄▄   ▄
+  █  █ ▄▄▄ █   █   ▄▄▄ ▄ ▄█ █  █  █▀▄▀█
+  █▀▀▄ █▄█ █▀  █▀  █▄█ █▀  █   █  █   █
+  █▄▄▀ ▀▄▄ ▀▄▄ ▀▄▄ ▀▄▄ █   █   █  █   █
+
+          Made with ❤️ by Sv443
+  I welcome every contribution on GitHub!
+    https://github.com/Sv443/BetterYTM
+  */
+
+  /* Disclaimer: I am not affiliated with YouTube, Google, Alphabet, Genius or anyone else */
+  /* C&D this 🖕 */
+  `;
+
   try {
     const rootPath = join(dirname(fileURLToPath(import.meta.url)), "../../");
     const lastCommitSha = await getLastCommitSha();
@@ -178,3 +184,20 @@ async function exists(path: string) {
     return false;
   }
 }
+
+/** Returns a string of resource directives, as defined in `assets/resources.json` or undefined if the file doesn't exist or is invalid */
+async function getResourceDirectives() {
+  try {
+    const directives: string[] = [];
+    const resourcesFile = String(await readFile(join(assetFolderPath, "resources.json")));
+    const resources = JSON.parse(resourcesFile) as Record<string, string>;
+
+    for(const [name, path] of Object.entries(resources))
+      directives.push(`// @resource        ${name} https://raw.githubusercontent.com/Sv443/BetterYTM/${branch}/assets/${path}`);
+
+    return directives.join("\n");
+  }
+  catch(err) {
+    console.warn("No resource directives found:", err);
+  }
+}

+ 5 - 4
src/utils.ts

@@ -1,6 +1,7 @@
 import { clamp, getUnsafeWindow, onSelector } from "@sv443-network/userutils";
-import { branch, scriptInfo } from "./constants";
+import { scriptInfo } from "./constants";
 import type { Domain, LogLevel } from "./types";
+import * as resources from "../assets/resources.json" assert { type: "json" };
 
 //#SECTION logging
 
@@ -222,7 +223,7 @@ export function getDomain(): Domain {
     throw new Error("BetterYTM is running on an unexpected website. Please don't tamper with the @match directives in the userscript header.");
 }
 
-/** Returns the URL of the asset hosted on GitHub at the specified relative `path` (starting at `{root}/assets/`) */
-export function getAssetUrl(path: string) {
-  return `https://raw.githubusercontent.com/Sv443/BetterYTM/${branch}/assets/${path}`;
+/** Returns the URL of a resource by its name, as defined in `assets/resources.json`, from GM resource cache - [see GM.getResourceUrl docs](https://wiki.greasespot.net/GM.getResourceUrl) */
+export function getResourceUrl(name: keyof typeof resources) {
+  return GM.getResourceUrl(name);
 }