Quellcode durchsuchen

feat: migrate to new persistent lyrics cache

Sv443 vor 1 Jahr
Ursprung
Commit
ea2b212059
9 geänderte Dateien mit 51 neuen und 38 gelöschten Zeilen
  1. 0 4
      assets/require.json
  2. 12 9
      contributing.md
  3. 2 2
      rollup.config.mjs
  4. 12 11
      src/features/lyrics.ts
  5. 6 6
      src/features/songLists.ts
  6. 2 0
      src/index.ts
  7. 3 3
      src/menu/menu_old.ts
  8. 7 0
      src/types.ts
  9. 7 3
      src/utils/misc.ts

+ 0 - 4
assets/require.json

@@ -1,8 +1,4 @@
 [
-  {
-    "pkgName": "@sv443-network/userutils",
-    "path": "dist/index.global.js"
-  },
   {
     "pkgName": "marked",
     "path": "lib/marked.umd.js"

+ 12 - 9
contributing.md

@@ -13,6 +13,8 @@ If you have any questions or need help, feel free to contact me, [see my homepag
   - [CLI commands](#these-are-the-cli-commands-available-after-setting-up-the-project)
   - [Extras](#extras)
 - [Developing a plugin that interfaces with BetterYTM](#developing-a-plugin-that-interfaces-with-betterytm)
+  - [Shimming for TypeScript without errors & with autocomplete](#shimming-for-typescript-without-errors--with-autocomplete)
+  - [Global functions on the plugin interface](#global-functions)
 
 <br><br>
 
@@ -263,10 +265,10 @@ The usage and example blocks on each are written in TypeScript but can be used i
   - [getFeatures()](#getfeatures) - Returns the current BYTM feature configuration object
   - [saveFeatures()](#savefeatures) - Overwrites the current BYTM feature configuration object with the provided one
 - Lyrics:
-  - [fetchLyricsUrl](#fetchlyricsurl) - Fetches the URL to the lyrics page for the specified song
-  - [getLyricsCacheEntry](#getlyricscacheentry) - Tries to find a URL entry in the in-memory cache for the specified song
-  - [sanitizeArtists](#sanitizeartists) - Sanitizes the specified artist string to be used in fetching a lyrics URL
-  - [sanitizeSong](#sanitizesong) - Sanitizes the specified song title string to be used in fetching a lyrics URL
+  - [fetchLyricsUrl()](#fetchlyricsurl) - Fetches the URL to the lyrics page for the specified song
+  - [getLyricsCacheEntry()](#getlyricscacheentry) - Tries to find a URL entry in the in-memory cache for the specified song
+  - [sanitizeArtists()](#sanitizeartists) - Sanitizes the specified artist string to be used in fetching a lyrics URL
+  - [sanitizeSong()](#sanitizesong) - Sanitizes the specified song title string to be used in fetching a lyrics URL
 
 <br>
 
@@ -302,7 +304,7 @@ The usage and example blocks on each are written in TypeScript but can be used i
 > #### getSessionId()
 > Usage:  
 > ```ts
-> unsafeWindow.BYTM.getSessionId(): string
+> unsafeWindow.BYTM.getSessionId(): string | null
 > ```
 >   
 > Description:  
@@ -638,11 +640,12 @@ The usage and example blocks on each are written in TypeScript but can be used i
 > #### getLyricsCacheEntry()
 > Usage:
 > ```ts
-> unsafeWindow.BYTM.getLyricsCacheEntry(artists: string, song: string): string | undefined
+> unsafeWindow.BYTM.getLyricsCacheEntry(artists: string, song: string): LyricsCacheEntry | undefined
 > ```
 >   
 > Description:  
 > Tries to find an entry in the in-memory cache for the specified song.  
+> You can find the structure of the `LyricsCacheEntry` type in the file [`src/types.ts`](src/types.ts)  
 > Contrary to [`fetchLyricsUrl()`](#fetchlyricsurl), this function does not fetch anything new if there is no entry in the cache.  
 >   
 > Arguments:  
@@ -655,10 +658,10 @@ The usage and example blocks on each are written in TypeScript but can be used i
 > 
 > ```ts
 > function tryToGetLyricsUrl() {
->   const lyricsUrl = unsafeWindow.BYTM.getLyricsCacheEntry("Michael Jackson", "Thriller");
+>   const lyricsEntry = unsafeWindow.BYTM.getLyricsCacheEntry("Michael Jackson", "Thriller");
 > 
->   if(lyricsUrl)
->     console.log(`The lyrics URL for Michael Jackson's Thriller is '${lyricsUrl}'`);
+>   if(lyricsEntry)
+>     console.log(`The lyrics URL for Michael Jackson's Thriller is '${lyricsEntry.url}'`);
 >   else
 >     console.log("Couldn't find the lyrics URL for this song in cache");
 > }

+ 2 - 2
rollup.config.mjs

@@ -54,7 +54,7 @@ export default (/**@type {import("./src/types").RollupArgs}*/ args) => (async ()
       compact: mode === "development",
       globals: {
         "marked": "marked",
-        "@sv443-network/userutils": "UserUtils",
+        // "@sv443-network/userutils": "UserUtils",
       },
     },
     onwarn(warning) {
@@ -66,7 +66,7 @@ export default (/**@type {import("./src/types").RollupArgs}*/ args) => (async ()
     },
     external: [
       "marked",
-      "@sv443-network/userutils",
+      // "@sv443-network/userutils",
     ],
   };
 

+ 12 - 11
src/features/lyrics.ts

@@ -1,7 +1,8 @@
-import { ConfigManager, clamp, fetchAdvanced, insertAfter } from "@sv443-network/userutils";
+import { ConfigManager, clamp, compress, decompress, fetchAdvanced, insertAfter } from "@sv443-network/userutils";
 import { constructUrlString, error, getResourceUrl, info, log, onSelectorOld, warn, t, tp } from "../utils";
 import { emitInterface } from "../interface";
-import { scriptInfo } from "../constants";
+import { compressionFormat, scriptInfo } from "../constants";
+import type { LyricsCacheEntry } from "../types";
 
 /** Base URL of geniURL */
 export const geniUrlBase = "https://api.sv443.net/geniurl";
@@ -24,13 +25,6 @@ void thresholdParam; // TODO: re-add once geniURL 1.4 is released
 /** How many cache entries can exist at a time - this is used to cap memory usage */
 const maxLyricsCacheSize = 300;
 
-export type LyricsCacheEntry = {
-  artist: string;
-  song: string;
-  url: string;
-  added: number;
-};
-
 export type LyricsCache = {
   cache: LyricsCacheEntry[];
 };
@@ -41,17 +35,19 @@ const lyricsCache = new ConfigManager<LyricsCache>({
     cache: [],
   },
   formatVersion: 1,
+  encodeData: (data) => compress(data, compressionFormat, "base64"),
+  decodeData: (data) => decompress(data, compressionFormat, "base64"),
   // migrations: {
   //   // 1 -> 2
   //   2: (oldData: Record<string, unknown>) => {
   //     return {
-  //       ...oldData,
+  //       cache: oldData.cache,
   //     };
   //   },
   // }
 });
 
-export async function initLyricsCacheNew() {
+export async function initLyricsCache() {
   const data = await lyricsCache.loadData();
   log(`Loaded lyrics cache (${data.cache.length} entries):`, data);
   return data;
@@ -66,6 +62,11 @@ export function getLyricsCacheEntry(artist: string, song: string) {
   return cache.find(e => e.artist === artist && e.song === song);
 }
 
+/** Returns the full lyrics cache array */
+export function getLyricsCache() {
+  return lyricsCache.getData().cache;
+}
+
 /**
  * Adds the provided entry into the lyrics URL cache, synchronously to RAM and asynchronously to GM storage  
  * {@linkcode artist} and {@linkcode song} need to be sanitized first!

+ 6 - 6
src/features/songLists.ts

@@ -150,25 +150,25 @@ async function addQueueButtons(
       const songSan = sanitizeSong(song);
       const splitTitle = splitVideoTitle(songSan);
 
-      const cachedLyricsUrl = songSan.includes("-")
+      const cachedLyricsEntry = songSan.includes("-")
         ? getLyricsCacheEntry(splitTitle.artist, splitTitle.song)
         : getLyricsCacheEntry(artistsSan, songSan);
 
-      if(cachedLyricsUrl)
-        lyricsUrl = cachedLyricsUrl;
+      if(cachedLyricsEntry)
+        lyricsUrl = cachedLyricsEntry.url;
       else if(!queueItem.hasAttribute("data-bytm-loading")) {
         const imgEl = lyricsBtnElem?.querySelector<HTMLImageElement>("img");
         if(!imgEl)
           return;
 
-        if(!cachedLyricsUrl) {
+        if(!cachedLyricsEntry) {
           queueItem.setAttribute("data-bytm-loading", "");
 
           imgEl.src = await getResourceUrl("img-spinner");
           imgEl.classList.add("bytm-spinner");
         }
 
-        lyricsUrl = cachedLyricsUrl ?? await fetchLyricsUrl(artistsSan, songSan);
+        lyricsUrl = cachedLyricsEntry ?? await fetchLyricsUrl(artistsSan, songSan);
 
         if(lyricsUrl) {
           emitInterface("bytm:lyricsLoaded", {
@@ -184,7 +184,7 @@ async function addQueueButtons(
           imgEl.classList.remove("bytm-spinner");
         };
 
-        if(!cachedLyricsUrl) {
+        if(!cachedLyricsEntry) {
           queueItem.removeAttribute("data-bytm-loading");
 
           // so the new image doesn't "blink"

+ 2 - 0
src/index.ts

@@ -36,6 +36,7 @@ import {
   addConfigMenuOption,
   // other
   checkVersion,
+  initLyricsCache,
 } from "./features/index";
 
 {
@@ -90,6 +91,7 @@ async function init() {
     });
 
     const features = await initConfig();
+    await initLyricsCache();
 
     await initTranslations(features.locale ?? "en_US");
     setLocale(features.locale ?? "en_US");

+ 3 - 3
src/menu/menu_old.ts

@@ -835,14 +835,14 @@ async function addExportMenu() {
   textAreaElem.readOnly = true;
   const cfgString = JSON.stringify({ formatVersion, data: getFeatures() });
   lastUncompressedCfgString = JSON.stringify({ formatVersion, data: getFeatures() }, undefined, 2);
-  textAreaElem.value = canCompress ? await compress(cfgString, compressionFormat) : cfgString;
+  textAreaElem.value = canCompress ? await compress(cfgString, compressionFormat, "base64") : cfgString;
 
   siteEvents.on("configChanged", async (data) => {
     const textAreaElem = document.querySelector<HTMLTextAreaElement>("#bytm-export-menu-textarea");
     const cfgString = JSON.stringify({ formatVersion, data });
     lastUncompressedCfgString = JSON.stringify({ formatVersion, data }, undefined, 2);
     if(textAreaElem)
-      textAreaElem.value = canCompress ? await compress(cfgString, compressionFormat) : cfgString;
+      textAreaElem.value = canCompress ? await compress(cfgString, compressionFormat, "base64") : cfgString;
   });
 
   //#SECTION footer
@@ -1037,7 +1037,7 @@ async function addImportMenu() {
         }
         catch {
           try {
-            return JSON.parse(await decompress(input, compressionFormat));
+            return JSON.parse(await decompress(input, compressionFormat, "base64"));
           }
           catch(err) {
             warn("Couldn't import configuration:", err);

+ 7 - 0
src/types.ts

@@ -40,6 +40,13 @@ export type HotkeyObj = {
 
 export type ObserverName = "body" | "playerBar" | "playerBarInfo";
 
+export type LyricsCacheEntry = {
+  artist: string;
+  song: string;
+  url: string;
+  added: number;
+};
+
 /** All functions exposed by the interface on the global `BYTM` object */
 export type InterfaceFunctions = {
   /** Adds a listener to one of the already present SelectorObserver instances */

+ 7 - 3
src/utils/misc.ts

@@ -36,14 +36,18 @@ export function getSessionId(): string | null {
   }
 }
 
+let isCompressionSupported: boolean | undefined;
+
 /** Tests whether compression via the predefined {@linkcode compressionFormat} is supported */
 export async function compressionSupported() {
+  if(typeof isCompressionSupported === "boolean")
+    return isCompressionSupported;
   try {
-    await compress(".", compressionFormat);
-    return true;
+    await compress(".", compressionFormat, "base64");
+    return isCompressionSupported = true;
   }
   catch(e) {
-    return false;
+    return isCompressionSupported = false;
   }
 }