Parcourir la source

feat: new lyrics cache

Sv443 il y a 1 an
Parent
commit
64cb0c073e
4 fichiers modifiés avec 72 ajouts et 32 suppressions
  1. 4 0
      src/constants.ts
  2. 53 16
      src/features/lyrics.ts
  3. 2 14
      src/menu/menu_old.ts
  4. 13 2
      src/utils/misc.ts

+ 4 - 0
src/constants.ts

@@ -13,12 +13,16 @@ export const repo = "Sv443/BetterYTM";
 /** Which host the userscript was installed from */
 export const host = (hostRaw.match(/^#{{.+}}$/) ? "github" : hostRaw) as "github" | "greasyfork" | "openuserjs";
 
+/** Names of platforms by value of {@linkcode host} */
 export const platformNames: Record<typeof host, string> = {
   github: "GitHub",
   greasyfork: "GreasyFork",
   openuserjs: "OpenUserJS",
 };
 
+/** Default compression format used throughout BYTM */
+export const compressionFormat: CompressionFormat = "deflate-raw";
+
 /**
  * How much info should be logged to the devtools console  
  * 0 = Debug (show everything) or 1 = Info (show only important stuff)

+ 53 - 16
src/features/lyrics.ts

@@ -1,4 +1,4 @@
-import { clamp, fetchAdvanced, insertAfter } from "@sv443-network/userutils";
+import { ConfigManager, clamp, 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";
@@ -19,27 +19,64 @@ const geniUrlRatelimitTimeframe = 30;
 const thresholdParam = threshold ? `&threshold=${clamp(threshold, 0, 1)}` : "";
 void thresholdParam; // TODO: re-add once geniURL 1.4 is released
 
-//#MARKER cache
+//#MARKER new cache
 
-/** Cache with key format `ARTIST - SONG` (sanitized) and lyrics URLs as values. Used to prevent extraneous requests to geniURL. */
-const lyricsUrlCache = new Map<string, string>();
 /** How many cache entries can exist at a time - this is used to cap memory usage */
-const maxLyricsCacheSize = 100;
+const maxLyricsCacheSize = 300;
+
+export type LyricsCacheEntry = {
+  artist: string;
+  song: string;
+  url: string;
+  added: number;
+};
+
+export type LyricsCache = {
+  cache: LyricsCacheEntry[];
+};
+
+const lyricsCache = new ConfigManager<LyricsCache>({
+  id: "bytm-lyrics-cache",
+  defaultConfig: {
+    cache: [],
+  },
+  formatVersion: 1,
+  // migrations: {
+  //   // 1 -> 2
+  //   2: (oldData: Record<string, unknown>) => {
+  //     return {
+  //       ...oldData,
+  //     };
+  //   },
+  // }
+});
+
+export async function initLyricsCacheNew() {
+  const data = await lyricsCache.loadData();
+  log(`Loaded lyrics cache (${data.cache.length} entries):`, data);
+  return data;
+}
 
 /**
- * Returns the lyrics URL from the passed un-/sanitized artist and song name, or undefined if the entry doesn't exist yet.  
- * **The passed parameters need to be sanitized first!**
+ * Returns the cache entry for the passed artist and song, or undefined if it doesn't exist yet  
+ * {@linkcode artist} and {@linkcode song} need to be sanitized first!
  */
-export function getLyricsCacheEntry(artists: string, song: string) {
-  return lyricsUrlCache.get(`${artists} - ${song}`);
+export function getLyricsCacheEntry(artist: string, song: string) {
+  const { cache } = lyricsCache.getData();
+  return cache.find(e => e.artist === artist && e.song === song);
 }
 
-/** Adds the provided entry into the lyrics URL cache */
-export function addLyricsCacheEntry(artists: string, song: string, lyricsUrl: string) {
-  lyricsUrlCache.set(`${sanitizeArtists(artists)} - ${sanitizeSong(song)}`, lyricsUrl);
-  // delete oldest entry if cache gets too big
-  if(lyricsUrlCache.size > maxLyricsCacheSize)
-    lyricsUrlCache.delete([...lyricsUrlCache.keys()].at(-1)!);
+/**
+ * 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!
+ */
+export function addLyricsCacheEntry(artist: string, song: string, lyricsUrl: string) {
+  const { cache } = lyricsCache.getData();
+  cache.push({ artist, song, url: lyricsUrl, added: Date.now() } satisfies LyricsCacheEntry);
+  cache.sort((a, b) => b.added - a.added);
+  if(cache.length > maxLyricsCacheSize)
+    cache.pop();
+  return lyricsCache.setData({ cache });
 }
 
 //#MARKER media control bar
@@ -218,7 +255,7 @@ export async function fetchLyricsUrl(artist: string, song: string): Promise<stri
     const cacheEntry = getLyricsCacheEntry(artist, song);
     if(cacheEntry) {
       info(`Found lyrics URL in cache: ${cacheEntry}`);
-      return cacheEntry;
+      return cacheEntry.url;
     }
 
     const startTs = Date.now();

+ 2 - 14
src/menu/menu_old.ts

@@ -1,8 +1,8 @@
 import { compress, decompress, debounce, isScrollable } from "@sv443-network/userutils";
 import { defaultConfig, getFeatures, migrations, saveFeatures, setDefaultFeatures } from "../config";
-import { host, scriptInfo } from "../constants";
+import { compressionFormat, host, scriptInfo } from "../constants";
 import { featInfo, disableBeforeUnload } from "../features/index";
-import { error, getResourceUrl, info, log, resourceToHTMLString, warn, getLocale, hasKey, initTranslations, setLocale, t, parseMarkdown, getChangelogMd } from "../utils";
+import { error, getResourceUrl, info, log, resourceToHTMLString, warn, getLocale, hasKey, initTranslations, setLocale, t, parseMarkdown, getChangelogMd, compressionSupported } from "../utils";
 import { formatVersion } from "../config";
 import { emitSiteEvent, siteEvents } from "../siteEvents";
 import type { FeatureCategory, FeatureKey, FeatureConfig, HotkeyObj, FeatureInfo } from "../types";
@@ -15,18 +15,6 @@ import pkg from "../../package.json" assert { type: "json" };
 let isCfgMenuAdded = false;
 export let isCfgMenuOpen = false;
 
-const compressionFormat: CompressionFormat = "deflate-raw";
-
-async function compressionSupported() {
-  try {
-    await compress(".", compressionFormat);
-    return true;
-  }
-  catch(e) {
-    return false;
-  }
-}
-
 /** Threshold in pixels from the top of the options container that dictates for how long the scroll indicator is shown */
 const scrollIndicatorOffsetThreshold = 30;
 let scrollIndicatorEnabled = true;

+ 13 - 2
src/utils/misc.ts

@@ -1,6 +1,6 @@
-import { fetchAdvanced, randomId } from "@sv443-network/userutils";
+import { compress, fetchAdvanced, randomId } from "@sv443-network/userutils";
 import { marked } from "marked";
-import { branch, repo } from "../constants";
+import { branch, compressionFormat, repo } from "../constants";
 import { type Domain, type ResourceKey } from "../types";
 import { error, type TrLocale, warn } from ".";
 import langMapping from "../../assets/locales.json" assert { type: "json" };
@@ -36,6 +36,17 @@ export function getSessionId(): string | null {
   }
 }
 
+/** Tests whether compression via the predefined {@linkcode compressionFormat} is supported */
+export async function compressionSupported() {
+  try {
+    await compress(".", compressionFormat);
+    return true;
+  }
+  catch(e) {
+    return false;
+  }
+}
+
 //#SECTION resources
 
 /**