A music player that connects to your cloud/distributed storage.

feat: output configurator

+101 -12
+86 -4
src/components/configurator/output/element.js
··· 2 2 import { computed, signal } from "@common/signal.js"; 3 3 4 4 /** 5 - * @import {ProxiedActions} from "@common/worker.d.ts" 6 5 * @import {Track} from "@definitions/types.d.ts" 7 6 * @import {OutputManager, OutputElement} from "@components/output/types.d.ts" 8 7 */ 9 8 9 + /** 10 + * @typedef {OutputElement<Track[]>} Output 11 + */ 12 + 13 + const STORAGE_PREFIX = "diffuse/configurator/output"; 14 + 10 15 //////////////////////////////////////////// 11 16 // ELEMENT 12 17 //////////////////////////////////////////// ··· 16 21 */ 17 22 class OutputConfigurator extends DiffuseElement { 18 23 static NAME = "diffuse/configurator/output"; 19 - static WORKER_URL = "components/configurator/output/worker.js"; 20 24 21 25 constructor() { 22 26 super(); ··· 25 29 const manager = { 26 30 tracks: { 27 31 collection: computed(() => { 32 + const out = this.#selectedOutput.value; 33 + if (out) return out.tracks.collection(); 28 34 return this.#memory.tracks.value; 29 35 }), 30 - reload: async () => {}, 36 + reload: () => { 37 + const out = this.#selectedOutput.value; 38 + if (out) return out.tracks.reload(); 39 + return Promise.resolve(); 40 + }, 31 41 save: async (newTracks) => { 42 + const out = this.#selectedOutput.value; 43 + if (out) return await out.tracks.save(newTracks); 32 44 this.#memory.tracks.value = newTracks; 33 45 }, 34 - state: () => "loaded", 46 + state: computed(() => { 47 + const out = this.#selectedOutput.value; 48 + if (out) return out.tracks.state(); 49 + return out === undefined ? "loading" : "loaded"; 50 + }), 35 51 }, 36 52 }; 37 53 ··· 45 61 tracks: signal(/** @type {Track[]} */ ([])), 46 62 }; 47 63 64 + #selectedOutput = signal( 65 + /** @type {Output | null | undefined} */ (undefined), 66 + ); 67 + 48 68 // LIFECYCLE 49 69 50 70 /** ··· 52 72 */ 53 73 async connectedCallback() { 54 74 super.connectedCallback(); 75 + this.#selectedOutput.value = await this.#findSelectedOutput(); 76 + 77 + this.effect(() => { 78 + console.log("selectedOutput changed", this.#selectedOutput.value); 79 + }); 80 + 81 + this.effect(() => { 82 + console.log("collection changed", this.tracks.collection()); 83 + }); 84 + 85 + this.effect(() => { 86 + console.log("state changed", this.tracks.state()); 87 + }); 88 + } 89 + 90 + // MISC 91 + 92 + async #findSelectedOutput() { 93 + const id = localStorage.getItem(`${STORAGE_PREFIX}/selected/id`); 94 + const el = id ? this.root().querySelector(`#${id}`) : null; 95 + 96 + if (!el) return null; 97 + 98 + await customElements.whenDefined(el.localName); 99 + 100 + if ( 101 + "nameWithGroup" in el === false || 102 + "tracks" in el === false 103 + ) { 104 + return null; 105 + } 106 + 107 + return /** @type {Output} */ (/** @type {unknown} */ (el)); 108 + } 109 + 110 + /** 111 + * @override 112 + */ 113 + dependencies() { 114 + return Object.fromEntries( 115 + Array.from(this.children).flatMap((element) => { 116 + if (element.hasAttribute("id") === false) { 117 + console.warn( 118 + "Missing `id` for output configurator child element with `localName` '" + 119 + element.localName + "'", 120 + ); 121 + return []; 122 + } 123 + 124 + const d = /** @type {DiffuseElement} */ (element); 125 + return [[d.id, d]]; 126 + }), 127 + ); 128 + } 129 + 130 + // ADDITIONAL ACTIONS 131 + 132 + /** 133 + * @param {string} id 134 + */ 135 + selectOutput(id) { 136 + localStorage.setItem(`${STORAGE_PREFIX}/selected/id`, id); 55 137 } 56 138 } 57 139
+1 -1
src/components/output/polymorphic/indexed-db/constants.js
··· 1 - export const IDB_PREFIX = "@components/output/polymorphic/indexed-db"; 1 + export const IDB_PREFIX = "diffuse/output/polymorphic/indexed-db";
+4 -2
src/themes/webamp/index.js
··· 1 + import "@components/configurator/output/element.js"; 2 + import "@components/input/opensubsonic/element.js"; 3 + import "@components/input/s3/element.js"; 1 4 import "@components/orchestrator/process-tracks/element.js"; 2 5 import "@components/orchestrator/queue-tracks/element.js"; 3 - import "@components/input/opensubsonic/element.js"; 4 - import "@components/input/s3/element.js"; 5 6 import "@components/output/polymorphic/indexed-db/element.js"; 6 7 import "@components/processor/metadata/element.js"; 7 8 import "@components/transformer/output/string/json/element.js"; ··· 22 23 const queue = component(Queue); 23 24 24 25 globalThis.queue = queue; 26 + globalThis.output = document.querySelector("#output"); 25 27 26 28 //////////////////////////////////////////// 27 29 // 📡
+10 -5
src/themes/webamp/index.vto
··· 77 77 --> 78 78 <de-queue></de-queue> 79 79 80 - <!-- Inputs, Output & Processors --> 81 - <dop-indexed-db></dop-indexed-db> 80 + <!-- Processors --> 82 81 <dp-metadata></dp-metadata> 83 82 83 + <!-- Input --> 84 84 <dc-input id="input"> 85 85 <di-opensubsonic></di-opensubsonic> 86 86 <di-s3></di-s3> 87 87 </dc-input> 88 88 89 - <!-- Transformers --> 90 - <dtor-default id="output" output-selector="dtos-json"></dtor-default> 91 - <dtos-json output-selector="dop-indexed-db"></dtos-json> 89 + <!-- Output --> 90 + <dop-indexed-db id="idb-json-output" key="json"></dop-indexed-db> 91 + 92 + <dc-output> 93 + <dtos-json id="idb-json" output-selector="#idb-json-output"></dtos-json> 94 + </dc-output> 95 + 96 + <dtor-default id="output" output-selector="dc-output"></dtor-default> 92 97 93 98 <!-- Orchestrators --> 94 99 <do-process-tracks