Experiment to rebuild Diffuse using web applets.
1import * as Uint8 from "uint8arrays";
2import { createEndpoint, type MessageEndpoint } from "@remote-ui/rpc";
3import { xxh32 } from "xxh32";
4
5import type { Track } from "@applets/core/types";
6
7export function arrayShuffle<T>(array: Array<T>): Array<T> {
8 if (array.length === 0) {
9 return [];
10 }
11
12 array = [...array];
13
14 for (let index = array.length - 1; index > 0; index--) {
15 const randArr = crypto.getRandomValues(new Uint32Array(1));
16 const randVal = randArr[0] / 2 ** 32;
17 const newIndex = Math.floor(randVal * (index + 1));
18 [array[index], array[newIndex]] = [array[newIndex], array[index]];
19 }
20
21 return array;
22}
23
24export function cleanUndefinedValuesForTracks(tracks: Track[]): Track[] {
25 return tracks.map((track) => {
26 const t = { ...track };
27
28 if (t.tags) {
29 if ("album" in t.tags && t.tags.album === undefined) delete t.tags.album;
30 if ("artist" in t.tags && t.tags.artist === undefined) delete t.tags.artist;
31 if ("genre" in t.tags && t.tags.genre === undefined) delete t.tags.genre;
32 if ("year" in t.tags && t.tags.year === undefined) delete t.tags.year;
33
34 if ("of" in t.tags.disc && t.tags.disc.of === undefined) delete t.tags.disc.of;
35 if ("of" in t.tags.track && t.tags.track.of === undefined) delete t.tags.track.of;
36 }
37
38 return t;
39 });
40}
41
42export function comparable(value: unknown) {
43 return xxh32(JSON.stringify(value));
44}
45
46export const endpoint = createEndpoint;
47
48export function expose<T extends Record<string, any>>(actions: T): T {
49 createEndpoint<T>(self as MessageEndpoint).expose(actions);
50 return actions;
51}
52
53export function inIframe() {
54 return window.self !== window.top;
55}
56
57export function isPrimitive(test: unknown) {
58 return test !== Object(test);
59}
60
61export function jsonDecode<T>(a: any): T {
62 return JSON.parse(new TextDecoder().decode(a));
63}
64
65export function jsonEncode<T>(a: T): Uint8Array {
66 return new TextEncoder().encode(JSON.stringify(a));
67}
68
69export async function trackArtworkCacheId(track: Track): Promise<string> {
70 return await crypto.subtle
71 .digest("SHA-256", new TextEncoder().encode(track.uri))
72 .then((a) => Uint8.toString(new Uint8Array(a), "base64url"));
73}