From 844402617ebdf7a4da68749fdb6a1ed54b1ec5c8 Mon Sep 17 00:00:00 2001 From: NextFire Date: Tue, 20 Aug 2024 20:27:31 +0200 Subject: [PATCH] feat: replace cache.json with deno kv --- .github/workflows/ci.yml | 4 +- .gitignore | 3 +- music-rpc.ts | 120 +++++++++++++-------------------------- 3 files changed, 44 insertions(+), 83 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00e46d9..080a92e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - uses: denoland/setup-deno@v1 - name: Compile x86_64 run: | - deno compile --allow-env --allow-run --allow-net --allow-read --allow-write --unstable-ffi --allow-ffi --target x86_64-apple-darwin music-rpc.ts + deno compile --allow-env --allow-run --allow-net --allow-read --allow-write --unstable-ffi --allow-ffi --unstable-kv --target x86_64-apple-darwin music-rpc.ts mv music-rpc music-rpc-${{ steps.version.outputs.commit_sha }}-x86_64-apple-darwin - name: Upload x86_64 uses: actions/upload-artifact@v4 @@ -27,7 +27,7 @@ jobs: path: music-rpc-${{ steps.version.outputs.commit_sha }}-x86_64-apple-darwin - name: Compile aarch64 run: | - deno compile --allow-env --allow-run --allow-net --allow-read --allow-write --unstable-ffi --allow-ffi --target aarch64-apple-darwin music-rpc.ts + deno compile --allow-env --allow-run --allow-net --allow-read --allow-write --unstable-ffi --allow-ffi --unstable-kv --target aarch64-apple-darwin music-rpc.ts mv music-rpc music-rpc-${{ steps.version.outputs.commit_sha }}-aarch64-apple-darwin - name: Upload aarch64 uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index a6f6628..ba47419 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ -.DS_Store +/cache_v?.sqlite3* /music-rpc.log -/cache.json diff --git a/music-rpc.ts b/music-rpc.ts index 5a5c91e..6858e91 100755 --- a/music-rpc.ts +++ b/music-rpc.ts @@ -1,76 +1,28 @@ -#!/usr/bin/env deno run --allow-env --allow-run --allow-net --allow-read --allow-write --unstable-ffi --allow-ffi - +#!/usr/bin/env deno run --allow-env --allow-run --allow-net --allow-read --allow-write --unstable-ffi --allow-ffi --unstable-kv import type { Activity } from "https://deno.land/x/discord_rpc@0.3.2/mod.ts"; import { Client } from "https://deno.land/x/discord_rpc@0.3.2/mod.ts"; import type {} from "https://raw.githubusercontent.com/NextFire/jxa/v0.0.5/run/global.d.ts"; import { run } from "https://raw.githubusercontent.com/NextFire/jxa/v0.0.5/run/mod.ts"; import type { iTunes } from "https://raw.githubusercontent.com/NextFire/jxa/v0.0.5/run/types/core.d.ts"; -// Cache - -class Cache { - static VERSION = 5; - static CACHE_FILE = "cache.json"; - static #data: Map = new Map(); - - static get(key: string) { - return this.#data.get(key); - } - - static set(key: string, value: TrackExtras) { - this.#data.set(key, value); - this.saveCache(); - } - - static async loadCache() { - try { - const text = await Deno.readTextFile(this.CACHE_FILE); - const data = JSON.parse(text); - if (data.version !== this.VERSION) throw new Error("Old cache"); - this.#data = new Map(data.data); - } catch (err) { - console.error( - err, - `No valid ${this.CACHE_FILE} found, generating a new cache...` - ); - } - } - - static async saveCache() { - try { - await Deno.writeTextFile( - this.CACHE_FILE, - JSON.stringify({ - version: this.VERSION, - data: Array.from(this.#data.entries()), - }) - ); - } catch (err) { - console.error(err); - } - } -} - -// Main part - +//#region entrypoint const MACOS_VER = await getMacOSVersion(); const IS_APPLE_MUSIC = MACOS_VER >= 10.15; const APP_NAME: iTunesAppName = IS_APPLE_MUSIC ? "Music" : "iTunes"; const CLIENT_ID = IS_APPLE_MUSIC ? "773825528921849856" : "979297966739300416"; -const DEFAULT_TIMEOUT = 15e3; -start(); +const KV_VERSION = 0; +const kv = await Deno.openKv(`cache_v${KV_VERSION}.sqlite3`); -async function start() { - await Cache.loadCache(); - const rpc = new Client({ id: CLIENT_ID }); - while (true) { - try { - await main(rpc); - } catch (err) { - console.error(err); - await new Promise((resolve) => setTimeout(resolve, DEFAULT_TIMEOUT)); - } +const DEFAULT_TIMEOUT = 15e3; + +const rpc = new Client({ id: CLIENT_ID }); +while (true) { + try { + await main(rpc); + } catch (err) { + console.error(err); + await sleep(DEFAULT_TIMEOUT); } } @@ -79,12 +31,16 @@ async function main(rpc: Client) { console.log(rpc); while (true) { const timeout = await setActivity(rpc); - await new Promise((resolve) => setTimeout(resolve, timeout)); + await sleep(timeout); } } -// macOS/JXA functions +function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} +//#endregion +//#region macOS/JXA functions async function getMacOSVersion(): Promise { const cmd = new Deno.Command("sw_vers", { args: ["-productVersion"] }); const output = await cmd.output(); @@ -115,22 +71,23 @@ function getProps(): Promise { }; }, APP_NAME); } +//#endregion +//#region iTunes Search API async function getTrackExtras(props: iTunesProps): Promise { const { name, artist, album } = props; const cacheIndex = `${name} ${artist} ${album}`; - let infos = Cache.get(cacheIndex); + const entry = await kv.get(["extras", cacheIndex]); + let infos = entry.value; if (!infos) { infos = await _getTrackExtras(name, artist, album); - Cache.set(cacheIndex, infos); + await kv.set(["extras", cacheIndex], infos); } return infos; } -// iTunes Search API - async function _getTrackExtras( song: string, artist: string, @@ -174,20 +131,16 @@ async function _getTrackExtras( const iTunesUrl = result?.trackViewUrl ?? null; return { artworkUrl, iTunesUrl }; } +//#endregion -// MusicBrainz Artwork Getter - -const MB_EXCLUDED_NAMES = ["", "Various Artist"]; -const luceneEscape = (term: string) => - term.replace(/([+\-&|!(){}\[\]^"~*?:\\])/g, "\\$1"); -const removeParenthesesContent = (term: string) => - term.replace(/\([^)]*\)/g, "").trim(); - +//#region MusicBrainz async function _getMBArtwork( artist: string, song: string, album: string ): Promise { + const MB_EXCLUDED_NAMES = ["", "Various Artist"]; + const queryTerms = []; if (!MB_EXCLUDED_NAMES.every((elem) => artist.includes(elem))) { queryTerms.push( @@ -226,8 +179,16 @@ async function _getMBArtwork( return result; } -// Activity setter +function luceneEscape(term: string): string { + return term.replace(/([+\-&|!(){}\[\]^"~*?:\\])/g, "\\$1"); +} +function removeParenthesesContent(term: string): string { + return term.replace(/\([^)]*\)/g, "").trim(); +} +//#endregion + +//#region Activity setter async function setActivity(rpc: Client): Promise { const open = await isOpen(); console.log("isOpen:", open); @@ -321,14 +282,14 @@ async function setActivity(rpc: Client): Promise { * @param maxLength * @returns Formatted string */ -function formatStr(s: string, minLength = 2, maxLength = 128) { +function formatStr(s: string, minLength = 2, maxLength = 128): string { return s.length <= maxLength ? s.padEnd(minLength) : `${s.slice(0, maxLength - 3)}...`; } +//#endregion -// TypeScript - +//#region TypeScript type iTunesAppName = "iTunes" | "Music"; interface iTunesProps { @@ -365,3 +326,4 @@ interface MBReleaseLookupResponse { interface MBRelease { id: string; } +//#endregion