A music player that connects to your cloud/distributed storage.
at v4 163 lines 3.2 kB view raw
1import { Temporal } from "~/common/temporal.js"; 2import { xxh32r } from "xxh32/dist/raw.js"; 3 4/** 5 * @import {Track} from "~/definitions/types.d.ts" 6 */ 7 8/** 9 * @template T 10 * @param {Array<T>} array 11 * @returns Array<T> 12 */ 13export function arrayShuffle(array) { 14 if (array.length === 0) { 15 return []; 16 } 17 18 array = [...array]; 19 20 for (let index = array.length - 1; index > 0; index--) { 21 const randArr = crypto.getRandomValues(new Uint32Array(1)); 22 const randVal = randArr[0] / 2 ** 32; 23 const newIndex = Math.floor(randVal * (index + 1)); 24 [array[index], array[newIndex]] = [array[newIndex], array[index]]; 25 } 26 27 return array; 28} 29 30/** 31 * @param {string | undefined | null} value 32 */ 33export function boolAttr(value) { 34 return value === ""; 35} 36 37/** 38 * @param {string} a 39 * @param {string} b 40 */ 41export function compareTimestamps(a, b) { 42 return Temporal.Instant.compare( 43 Temporal.Instant.from(a), 44 Temporal.Instant.from(b), 45 ); 46} 47 48/** 49 * @param {any} object 50 */ 51export function hash(object) { 52 return xxh32r(jsonEncode(object)).toString(); 53} 54 55/** 56 * @param {Track[]} tracks 57 * @param {Record<string, Track[]>} initial 58 * @returns {Record<string, Track[]>} 59 */ 60export function groupTracksPerScheme( 61 tracks, 62 initial = {}, 63) { 64 /** @type {Record<string, Track[]>} */ 65 const acc = initial; 66 67 tracks.forEach((track) => { 68 const scheme = track.uri.substring(0, track.uri.indexOf(":")); 69 acc[scheme] ??= []; 70 acc[scheme].push(track); 71 }); 72 73 return acc; 74} 75 76/** 77 * @param {string[]} uris 78 * @returns {Record<string, string[]>} 79 */ 80export function groupUrisPerScheme(uris) { 81 /** @type {Record<string, string[]>} */ 82 const acc = {}; 83 84 uris.forEach((uri) => { 85 const scheme = uri.substring(0, uri.indexOf(":")); 86 acc[scheme] ??= []; 87 acc[scheme].push(uri); 88 }); 89 90 return acc; 91} 92 93/** 94 * @param {unknown} test 95 */ 96export function isPrimitive(test) { 97 return test !== Object(test); 98} 99 100/** 101 * @template T 102 * @param {any} a 103 * @returns {T} 104 */ 105export function jsonDecode(a) { 106 return JSON.parse(new TextDecoder().decode(a)); 107} 108 109/** 110 * @template T 111 * @param {T} a 112 * @returns Uint8Array 113 */ 114export function jsonEncode(a) { 115 return new TextEncoder().encode(JSON.stringify(a)); 116} 117 118/** 119 * @template {Record<string, any>} T 120 * @param {T} rec 121 */ 122export function removeUndefinedValuesFromRecord(rec) { 123 const recClone = { ...rec }; 124 125 Object.entries(recClone).forEach(([key, value]) => { 126 if (value === undefined) { 127 delete recClone[key]; 128 } 129 }); 130 131 return recClone; 132} 133 134/** 135 * @template {Record<string, any>} T 136 * @param {T} rec 137 */ 138export function recursivelyCloneRecords(rec) { 139 const recClone = { ...rec }; 140 141 Object.entries(recClone).forEach(([key, value]) => { 142 if (typeof value === "object") { 143 /** @ts-ignore */ 144 recClone[key] = recursivelyCloneRecords(value); 145 } 146 }); 147 148 return recClone; 149} 150 151/** 152 * @param {string} str 153 * @returns {string} 154 */ 155export function safeDecodeURIComponent(str) { 156 return str.replace( 157 /%u([0-9A-Fa-f]{4})|%([0-9A-Fa-f]{2})/g, 158 (_, unicode, byte) => 159 unicode 160 ? String.fromCharCode(parseInt(unicode, 16)) 161 : String.fromCharCode(parseInt(byte, 16)), 162 ); 163}