A music player that connects to your cloud/distributed storage.
at v4 121 lines 3.0 kB view raw
1import * as TID from "@atcute/tid"; 2import { DiffuseElement } from "~/common/element.js"; 3import { SCHEME } from "./constants.js"; 4import { 5 buildURI, 6 loadHandles, 7 saveHandles, 8 tidsFromTracks, 9} from "./common.js"; 10 11/** 12 * @import {InputActions, InputSchemeProvider} from "~/components/input/types.d.ts" 13 * @import {ProxiedActions} from "~/common/worker.d.ts" 14 * @import {Track} from "~/definitions/types.d.ts" 15 */ 16 17//////////////////////////////////////////// 18// ELEMENT 19//////////////////////////////////////////// 20 21/** 22 * @implements {ProxiedActions<InputActions>} 23 * @implements {InputSchemeProvider} 24 */ 25class LocalInput extends DiffuseElement { 26 static NAME = "diffuse/input/local"; 27 static WORKER_URL = "components/input/local/worker.js"; 28 29 SCHEME = SCHEME; 30 31 /** @type {Map<string, string>} tid → handle name */ 32 #names = new Map(); 33 34 constructor() { 35 super(); 36 37 /** @type {ProxiedActions<InputActions>} */ 38 this.proxy = this.workerProxy(); 39 40 this.artwork = this.proxy.artwork; 41 this.consult = this.proxy.consult; 42 this.detach = this.proxy.detach; 43 this.groupConsult = this.proxy.groupConsult; 44 this.list = this.proxy.list; 45 this.resolve = this.proxy.resolve; 46 } 47 48 // LIFECYCLE 49 50 /** @override */ 51 async connectedCallback() { 52 super.connectedCallback(); 53 const handles = await loadHandles(); 54 for (const [tid, handle] of Object.entries(handles)) { 55 this.#names.set(tid, handle.name); 56 } 57 } 58 59 // 🛠️ 60 61 /** 62 * Prompts the user to pick a directory. 63 * Stores handle in IDB. 64 * Returns the URI for the track placeholder. 65 */ 66 async addDirectory() { 67 const dirHandle = await /** @type {any} */ (globalThis).showDirectoryPicker( 68 { mode: "read" }, 69 ); 70 71 const tid = TID.now(); 72 const handles = await loadHandles(); 73 74 handles[tid] = dirHandle; 75 await saveHandles(handles); 76 this.#names.set(tid, dirHandle.name); 77 78 return buildURI(tid); 79 } 80 81 /** 82 * Prompts the user to pick one or more files. 83 * Stores handles in IDB. 84 * Returns the URIs for the track placeholders. 85 */ 86 async addFiles() { 87 const fileHandles = await /** @type {any} */ (globalThis) 88 .showOpenFilePicker({ multiple: true }); 89 const handles = await loadHandles(); 90 const uris = []; 91 92 for (const fileHandle of fileHandles) { 93 const tid = TID.now(); 94 handles[tid] = fileHandle; 95 this.#names.set(tid, fileHandle.name); 96 uris.push(buildURI(tid)); 97 } 98 99 await saveHandles(handles); 100 return uris; 101 } 102 103 /** @param {Track[]} tracks */ 104 sources(tracks) { 105 return Object.values(tidsFromTracks(tracks)).map((tid) => ({ 106 label: this.#names.get(tid) ?? tid, 107 uri: buildURI(tid), 108 })); 109 } 110} 111 112export default LocalInput; 113 114//////////////////////////////////////////// 115// REGISTER 116//////////////////////////////////////////// 117 118export const CLASS = LocalInput; 119export const NAME = "di-local"; 120 121customElements.define(NAME, CLASS);