A music player that connects to your cloud/distributed storage. diffuse.sh
Something went wrong. Try again.
3.9 kB · 173 lines
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174import * as IDB from "idb-keyval";import * as URI from "fast-uri";
import { isAudioFile } from "~/components/input/common.js";import { safeDecodeURIComponent } from "~/common/utils.js";import { IDB_HANDLES, SCHEME } from "./constants.js";
/** * @import { Track } from "~/definitions/types.d.ts" */
////////////////////////////////////////////// 🛠️////////////////////////////////////////////
/** * @param {string} tid * @param {string} [path] */export function buildURI(tid, path = "/") { return URI.serialize({ scheme: SCHEME, host: tid, path, });}
/** * @param {FileSystemDirectoryHandle} dirHandle * @param {string} [basePath] * @returns {Promise<string[]>} */export async function enumerateAudioFiles(dirHandle, basePath = "/") { const results = [];
for await (const [name, handle] of /** @type {any} */ (dirHandle).entries()) { const entryPath = basePath + name;
if (handle.kind === "directory") { const sub = await enumerateAudioFiles( /** @type {FileSystemDirectoryHandle} */ (handle), entryPath + "/", ); results.push(...sub); } else if (isAudioFile(name)) { results.push(entryPath); } }
return results;}
/** * @param {FileSystemHandle} handle * @param {string} path * @returns {Promise<FileSystemFileHandle>} */export async function getHandleFile(handle, path) { if (handle.kind === "file") { return /** @type {FileSystemFileHandle} */ (handle); }
const parts = path.replace(/^\//, "").split("/").filter(Boolean); let current = /** @type {FileSystemDirectoryHandle} */ (handle);
for (const part of parts.slice(0, -1)) { current = await current.getDirectoryHandle(part); }
return current.getFileHandle(/** @type {string} */ (parts.at(-1)));}
/** * @param {Track[]} tracks * @returns {Record<string, { tid: string; tracks: Track[] }>} */export function groupTracksByTid(tracks) { /** @type {Record<string, { tid: string; tracks: Track[] }>} */ const acc = {};
tracks.forEach((track) => { const parsed = parseURI(track.uri); if (!parsed) return;
const { tid } = parsed; if (acc[tid]) { acc[tid].tracks.push(track); } else { acc[tid] = { tid, tracks: [track] }; } });
return acc;}
/** * @param {string[]} uris * @returns {Record<string, { tid: string; uris: string[] }>} */export function groupUrisByTid(uris) { /** @type {Record<string, { tid: string; uris: string[] }>} */ const acc = {};
uris.forEach((uri) => { const parsed = parseURI(uri); if (!parsed) return;
const { tid } = parsed; if (acc[tid]) { acc[tid].uris.push(uri); } else { acc[tid] = { tid, uris: [uri] }; } });
return acc;}
export function isSupported() { return typeof (/** @type {any} */ (globalThis).showDirectoryPicker) !== "undefined";}
/** * @returns {Promise<Record<string, FileSystemHandle>>} */export async function loadHandles() { const i = await IDB.get(IDB_HANDLES); return i ?? {};}
/** * @param {string} uriString * @returns {{ tid: string; path: string } | undefined} */export function parseURI(uriString) { try { const url = new URL(uriString); if (url.protocol !== `${SCHEME}:`) return undefined; if (!url.host) return undefined;
return { tid: url.host, path: safeDecodeURIComponent(url.pathname), }; } catch { return undefined; }}
/** * @param {Record<string, FileSystemHandle>} handles */export async function saveHandles(handles) { await IDB.set(IDB_HANDLES, handles);}
/** * @param {Track[]} tracks * @returns {Record<string, string>} */export function tidsFromTracks(tracks) { /** @type {Record<string, string>} */ const acc = {};
tracks.forEach((track) => { const parsed = parseURI(track.uri); if (!parsed) return; acc[parsed.tid] = parsed.tid; });
return acc;}