A music player that connects to your cloud/distributed storage.
at main 2.7 kB view raw
1import { debounce } from "throttle-debounce" 2 3import type { App } from "./elm/types" 4import { type CoverPrep, db } from "../common" 5 6 7 8// 🏔️ 9 10 11let app: App 12let brain: Worker 13 14 15 16// 🚀 17 18 19export function init(a: App, b: Worker) { 20 app = a 21 brain = b 22 23 app.ports.loadAlbumCovers.subscribe( 24 debounce(500, loadAlbumCoversFromDom) 25 ) 26 27 db().keys().then(cachedCovers) 28} 29 30 31 32// 🛠️ 33 34 35export function albumCover(coverKey: string): Promise<Blob | "TRIED" | null> { 36 return db().getItem(`coverCache.${coverKey}`) 37} 38 39 40async function loadAlbumCoversFromDom({ coverView, list }: { coverView: boolean, list: boolean }): Promise<void> { 41 let nodes: HTMLElement[] = [] 42 43 if (list) nodes = nodes.concat(Array.from( 44 document.querySelectorAll("#diffuse__track-covers [data-key]") 45 )) 46 47 if (coverView) nodes = nodes.concat(Array.from( 48 document.querySelectorAll("#diffuse__track-covers + div [data-key]") 49 )) 50 51 if (!nodes.length) return; 52 53 const coverPrepList = nodes.reduce((acc: CoverPrep[], node: HTMLElement) => { 54 const a = { 55 cacheKey: node.getAttribute("data-key"), 56 trackFilename: node.getAttribute("data-filename"), 57 trackPath: node.getAttribute("data-path"), 58 trackSourceId: node.getAttribute("data-source-id"), 59 variousArtists: node.getAttribute("data-various-artists") 60 } 61 62 if (a.cacheKey && a.trackFilename && a.trackPath && a.trackSourceId && a.variousArtists) { 63 return [...acc, a as CoverPrep] 64 } else { 65 return acc 66 } 67 }, [] as CoverPrep[]) 68 69 return loadAlbumCovers(coverPrepList) 70} 71 72 73export async function loadAlbumCovers(coverPrepList: CoverPrep[]): Promise<void> { 74 const withoutEarlierAttempts = await coverPrepList.reduce(async ( 75 acc: Promise<CoverPrep[]>, 76 prep: CoverPrep 77 ): Promise<CoverPrep[]> => { 78 const arr = await acc 79 const a = await albumCover(prep.cacheKey) 80 if (!a) return [...arr, prep] 81 return arr 82 }, Promise.resolve([])) 83 84 brain.postMessage({ 85 action: "DOWNLOAD_ARTWORK", 86 data: withoutEarlierAttempts 87 }) 88} 89 90 91// Send a dictionary of the cached covers to the app. 92async function cachedCovers(keys: string[]) { 93 const cacheKeys = keys.filter( 94 k => k.startsWith("coverCache.") 95 ) 96 97 const cache = await cacheKeys.reduce(async (acc, key) => { 98 const c = await acc 99 const blob = await db().getItem(key) 100 const cacheKey = key.slice(11) 101 102 if (blob && typeof blob !== "string" && blob instanceof Blob) { 103 c[cacheKey] = URL.createObjectURL(blob) 104 } 105 106 return c 107 }, Promise.resolve({})) 108 109 app.ports.insertCoverCache.send(cache) 110 setTimeout(() => loadAlbumCoversFromDom({ list: true, coverView: true }), 500) 111}