Răsfoiți Sursa

fix: auto-refresh cache entries above 2 weeks old

Sv443 1 an în urmă
părinte
comite
9ce82922fe
4 a modificat fișierele cu 64 adăugiri și 13 ștergeri
  1. 36 5
      src/features/lyrics.ts
  2. 2 2
      src/features/songLists.ts
  3. 25 6
      src/index.ts
  4. 1 0
      src/types.ts

+ 36 - 5
src/features/lyrics.ts

@@ -24,6 +24,8 @@ 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;
+/** Maximum time before a cache entry is force deleted */
+const cacheTTL = 1000 * 60 * 60 * 24 * 7 * 2; // 2 weeks
 
 export type LyricsCache = {
   cache: LyricsCacheEntry[];
@@ -56,10 +58,39 @@ export async function initLyricsCache() {
 /**
  * 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!
+ * @param refreshEntry If true, the timestamp of the entry will be set to the current time
  */
-export function getLyricsCacheEntry(artist: string, song: string) {
+export function getLyricsCacheEntry(artist: string, song: string, refreshEntry = true) {
   const { cache } = lyricsCache.getData();
-  return cache.find(e => e.artist === artist && e.song === song);
+  const entry = cache.find(e => e.artist === artist && e.song === song);
+  if(entry && Date.now() - entry?.added > cacheTTL) {
+    deleteLyricsCacheEntry(artist, song);
+    return undefined;
+  }
+
+  // refresh timestamp of the entry by mutating cache
+  if(entry && refreshEntry)
+    updateLyricsCacheEntry(artist, song);
+  return entry;
+}
+
+function updateLyricsCacheEntry(artist: string, song: string) {
+  const { cache } = lyricsCache.getData();
+  const idx = cache.findIndex(e => e.artist === artist && e.song === song);
+  if(idx !== -1) {
+    const newEntry = cache.splice(idx, 1)[0]!;
+    newEntry.viewed = Date.now();
+    lyricsCache.setData({ cache: [ newEntry, ...cache ] });
+  }
+}
+
+function deleteLyricsCacheEntry(artist: string, song: string) {
+  const { cache } = lyricsCache.getData();
+  const idx = cache.findIndex(e => e.artist === artist && e.song === song);
+  if(idx !== -1) {
+    cache.splice(idx, 1);
+    lyricsCache.setData({ cache });
+  }
 }
 
 /** Returns the full lyrics cache array */
@@ -74,9 +105,9 @@ export function getLyricsCache() {
 export function addLyricsCacheEntry(artist: string, song: string, url: string) {
   const { cache } = lyricsCache.getData();
   cache.push({
-    artist, song, url, added: Date.now(),
+    artist, song, url, viewed: Date.now(), added: Date.now(),
   } satisfies LyricsCacheEntry);
-  cache.sort((a, b) => b.added - a.added);
+  cache.sort((a, b) => b.viewed - a.viewed);
   if(cache.length > maxLyricsCacheSize)
     cache.pop();
   return lyricsCache.setData({ cache });
@@ -257,7 +288,7 @@ export async function fetchLyricsUrl(artist: string, song: string): Promise<stri
   try {
     const cacheEntry = getLyricsCacheEntry(artist, song);
     if(cacheEntry) {
-      info(`Found lyrics URL in cache: ${cacheEntry}`);
+      info(`Found lyrics URL in cache: ${cacheEntry.url}`);
       return cacheEntry.url;
     }
 

+ 2 - 2
src/features/songLists.ts

@@ -3,7 +3,7 @@ import { clearInner, error, getResourceUrl, log, onSelectorOld, t, warn } from "
 import { SiteEventsMap, siteEvents } from "../siteEvents";
 import { emitInterface } from "../interface";
 import { fetchLyricsUrl, createLyricsBtn, sanitizeArtists, sanitizeSong, getLyricsCacheEntry, splitVideoTitle } from "./lyrics";
-import type { FeatureConfig } from "../types";
+import type { FeatureConfig, LyricsCacheEntry } from "../types";
 import "./songLists.css";
 
 let features: FeatureConfig;
@@ -168,7 +168,7 @@ async function addQueueButtons(
           imgEl.classList.add("bytm-spinner");
         }
 
-        lyricsUrl = cachedLyricsEntry ?? await fetchLyricsUrl(artistsSan, songSan);
+        lyricsUrl = (cachedLyricsEntry as unknown as LyricsCacheEntry)?.url ?? await fetchLyricsUrl(artistsSan, songSan);
 
         if(lyricsUrl) {
           emitInterface("bytm:lyricsLoaded", {

+ 25 - 6
src/index.ts

@@ -279,7 +279,7 @@ function registerMenuCommands() {
       }
     }, "r");
 
-    GM.registerMenuCommand("List GM values", async () => {
+    GM.registerMenuCommand("List GM values in console with decompression", async () => {
       const keys = await GM.listValues();
       console.log("GM values:");
       if(keys.length === 0)
@@ -294,13 +294,33 @@ function registerMenuCommands() {
         values[key] = typeof val !== "undefined" && isEncoded ? await decompress(val, compressionFormat, "string") : val;
         longestKey = Math.max(longestKey, key.length);
       }
-      for(const [key, val] of Object.entries(values)) {
+      for(const [key, finalVal] of Object.entries(values)) {
         const isEncoded = key.startsWith("_uucfg-") ? await GM.getValue(`_uucfgenc-${key.substring(7)}`, false) : false;
-        console.log(`  "${key}"${" ".repeat(longestKey - key.length)} -${isEncoded ? "-[decoded]-" : ""}> ${val}`);
+        const lengthStr = String(finalVal).length > 50 ? `(${String(finalVal).length} chars) ` : "";
+        console.log(`  "${key}"${" ".repeat(longestKey - key.length)} -${isEncoded ? "-[decoded]-" : ""}> ${lengthStr}${finalVal}`);
       }
-      alert("See console.");
     }, "l");
 
+    GM.registerMenuCommand("List GM values in console, without decompression", async () => {
+      const keys = await GM.listValues();
+      console.log("GM values:");
+      if(keys.length === 0)
+        console.log("  No values found.");
+
+      const values = {} as Record<string, Stringifiable | undefined>;
+      let longestKey = 0;
+
+      for(const key of keys) {
+        const val = await GM.getValue(key, undefined);
+        values[key] = val;
+        longestKey = Math.max(longestKey, key.length);
+      }
+      for(const [key, val] of Object.entries(values)) {
+        const lengthStr = String(val).length >= 16 ? `(${String(val).length} chars) ` : "";
+        console.log(`  "${key}"${" ".repeat(longestKey - key.length)} -> ${lengthStr}${val}`);
+      }
+    });
+
     GM.registerMenuCommand("Delete all GM values", async () => {
       if(confirm("Clear all GM values?\nSee console for details.")) {
         const keys = await GM.listValues();
@@ -333,7 +353,7 @@ function registerMenuCommands() {
       console.log("Reset version check time.");
     }, "v");
 
-    GM.registerMenuCommand("List active selector listeners", async () => {
+    GM.registerMenuCommand("List active selector listeners in console", async () => {
       const lines = [] as string[];
       let listenersAmt = 0;
       for(const [obsName, obs] of Object.entries(observers)) {
@@ -348,7 +368,6 @@ function registerMenuCommands() {
         });
       }
       console.log(`Showing currently active listeners for ${Object.keys(observers).length} observers with ${listenersAmt} total listeners:\n${lines.join("\n")}`);
-      alert("See console.");
     }, "s");
   }
 }

+ 1 - 0
src/types.ts

@@ -44,6 +44,7 @@ export type LyricsCacheEntry = {
   artist: string;
   song: string;
   url: string;
+  viewed: number;
   added: number;
 };