import * as Uint8 from "uint8arrays"; import * as Comlink from "comlink"; import { xxh32 } from "xxh32"; import { getTransferables } from "@okikio/transferables"; import type { Track } from "@applets/core/types"; // export { SharedWorkerPolyfill as SharedWorker } from "@okikio/sharedworker"; export const SharedWorker = globalThis.SharedWorker; export function arrayShuffle(array: Array): Array { if (array.length === 0) { return []; } array = [...array]; for (let index = array.length - 1; index > 0; index--) { const randArr = crypto.getRandomValues(new Uint32Array(1)); const randVal = randArr[0] / 2 ** 32; const newIndex = Math.floor(randVal * (index + 1)); [array[index], array[newIndex]] = [array[newIndex], array[index]]; } return array; } export function cleanUndefinedValuesForTracks(tracks: Track[]): Track[] { return tracks.map((track) => { const t = { ...track }; if (t.tags) { if ("album" in t.tags && t.tags.album === undefined) delete t.tags.album; if ("artist" in t.tags && t.tags.artist === undefined) delete t.tags.artist; if ("genre" in t.tags && t.tags.genre === undefined) delete t.tags.genre; if ("year" in t.tags && t.tags.year === undefined) delete t.tags.year; if ("of" in t.tags.disc && t.tags.disc.of === undefined) delete t.tags.disc.of; if ("of" in t.tags.track && t.tags.track.of === undefined) delete t.tags.track.of; } return t; }); } export function comparable(value: unknown) { return xxh32(JSON.stringify(value)); } export function endpoint>(ini: Comlink.Endpoint) { const e = Comlink.wrap(ini); if ("start" in ini && typeof ini.start === "function") ini.start(); return e; } export function expose>(actions: T): T { if (globalThis.SharedWorkerGlobalScope && self instanceof SharedWorkerGlobalScope) { self.onconnect = (event: MessageEvent) => { const port = event.ports[0]; Comlink.expose(actions, port); port.start(); }; (self as any).connected = true; } else { Comlink.expose(actions, self); } return actions; } export function groupTracksPerScheme( tracks: Track[], initial: Record = {}, ): Record { return tracks.reduce((acc: Record, track: Track) => { const scheme = track.uri.split(":", 1)[0]; return { ...acc, [scheme]: [...(acc[scheme] || []), track] }; }, initial); } export function inIframe() { return window.self !== window.top; } export function isPrimitive(test: unknown) { return test !== Object(test); } export function jsonDecode(a: any): T { return JSON.parse(new TextDecoder().decode(a)); } export function jsonEncode(a: T): Uint8Array { return new TextEncoder().encode(JSON.stringify(a)); } export async function trackArtworkCacheId(track: Track): Promise { return await crypto.subtle .digest("SHA-256", new TextEncoder().encode(track.uri)) .then((a) => Uint8.toString(new Uint8Array(a), "base64url")); } export function transfer(a: T) { const b = getTransferables(a); return Comlink.transfer(a, b); }