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}