Experiment to rebuild Diffuse using web applets.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at de9fcaf778fa5e048b20dbdce8527259b86cfa45 94 lines 2.8 kB view raw
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 { SharedWorkerPolyfill as SharedWorker } from "@okikio/sharedworker"; 8 9export function arrayShuffle<T>(array: Array<T>): Array<T> { 10 if (array.length === 0) { 11 return []; 12 } 13 14 array = [...array]; 15 16 for (let index = array.length - 1; index > 0; index--) { 17 const randArr = crypto.getRandomValues(new Uint32Array(1)); 18 const randVal = randArr[0] / 2 ** 32; 19 const newIndex = Math.floor(randVal * (index + 1)); 20 [array[index], array[newIndex]] = [array[newIndex], array[index]]; 21 } 22 23 return array; 24} 25 26export function cleanUndefinedValuesForTracks(tracks: Track[]): Track[] { 27 return tracks.map((track) => { 28 const t = { ...track }; 29 30 if (t.tags) { 31 if ("album" in t.tags && t.tags.album === undefined) delete t.tags.album; 32 if ("artist" in t.tags && t.tags.artist === undefined) delete t.tags.artist; 33 if ("genre" in t.tags && t.tags.genre === undefined) delete t.tags.genre; 34 if ("year" in t.tags && t.tags.year === undefined) delete t.tags.year; 35 36 if ("of" in t.tags.disc && t.tags.disc.of === undefined) delete t.tags.disc.of; 37 if ("of" in t.tags.track && t.tags.track.of === undefined) delete t.tags.track.of; 38 } 39 40 return t; 41 }); 42} 43 44export function comparable(value: unknown) { 45 return xxh32(JSON.stringify(value)); 46} 47 48export function endpoint<T extends Record<string, any>>(port: MessagePort) { 49 const e = createEndpoint<T>(port); 50 port.start(); 51 return e; 52} 53 54export function expose<T extends Record<string, any>>(actions: T): T { 55 (self as unknown as SharedWorkerGlobalScope).onconnect = (event: MessageEvent) => { 56 const port = event.ports[0]; 57 createEndpoint<T>(port).expose(actions); 58 port.start(); 59 }; 60 61 return actions; 62} 63 64export function groupTracksPerScheme( 65 tracks: Track[], 66 initial: Record<string, Track[]> = {}, 67): Record<string, Track[]> { 68 return tracks.reduce((acc: Record<string, Track[]>, track: Track) => { 69 const scheme = track.uri.split(":", 1)[0]; 70 return { ...acc, [scheme]: [...(acc[scheme] || []), track] }; 71 }, initial); 72} 73 74export function inIframe() { 75 return window.self !== window.top; 76} 77 78export function isPrimitive(test: unknown) { 79 return test !== Object(test); 80} 81 82export function jsonDecode<T>(a: any): T { 83 return JSON.parse(new TextDecoder().decode(a)); 84} 85 86export function jsonEncode<T>(a: T): Uint8Array { 87 return new TextEncoder().encode(JSON.stringify(a)); 88} 89 90export async function trackArtworkCacheId(track: Track): Promise<string> { 91 return await crypto.subtle 92 .digest("SHA-256", new TextEncoder().encode(track.uri)) 93 .then((a) => Uint8.toString(new Uint8Array(a), "base64url")); 94}