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

Configure Feed

Select the types of activity you want to include in your feed.

feat: webamp input configurator source creation

+170 -23
+17 -1
src/common/constituents/default.js
··· 1 1 import Queue from "@components/engine/queue/element.js"; 2 2 import InputOrchestrator from "@components/orchestrator/input/element.js"; 3 3 import OutputOrchestrator from "@components/orchestrator/output/element.js"; 4 + import MetadataProcessor from "@components/processor/metadata/element.js"; 5 + import ProcessTracksOrchestrator from "@components/orchestrator/process-tracks/element.js"; 4 6 import QueueTracksOrchestrator from "@components/orchestrator/queue-tracks/element.js"; 5 7 import RepeatShuffleOrchestrator from "@components/orchestrator/repeat-shuffle/element.js"; 6 8 import SearchProcessor from "@components/processor/search/element.js"; ··· 31 33 document.body.append(output); 32 34 33 35 // Processors 36 + const metadata = new MetadataProcessor(); 37 + metadata.setAttribute("group", GROUP); 38 + 39 + document.body.append(metadata); 40 + 34 41 const search = new SearchProcessor(); 35 42 search.setAttribute("group", GROUP); 36 43 37 44 document.body.append(search); 38 45 39 46 // Orchestrators 47 + const opt = new ProcessTracksOrchestrator(); 48 + opt.setAttribute("group", GROUP); 49 + opt.setAttribute("input-selector", "#input"); 50 + opt.setAttribute("output-selector", "#output"); 51 + opt.setAttribute("metadata-processor-selector", metadata.localName); 52 + opt.toggleAttribute("process-when-ready"); 53 + 40 54 const oqt = new QueueTracksOrchestrator(); 41 55 oqt.setAttribute("group", GROUP); 42 56 oqt.setAttribute("input-selector", "#input"); ··· 53 67 ost.setAttribute("output-selector", "#output"); 54 68 ost.setAttribute("search-processor-selector", search.localName); 55 69 56 - document.body.append(oqt, rso, ost); 70 + document.body.append(opt, oqt, rso, ost); 57 71 58 72 // Return elements 59 73 return { ··· 69 83 orchestrator: { 70 84 input, 71 85 output, 86 + processTracks: opt, 72 87 queueTracks: oqt, 73 88 repeatShuffle: rso, 74 89 }, 75 90 processor: { 91 + metadata, 76 92 search, 77 93 }, 78 94 };
+8 -4
src/components/orchestrator/queue-audio/element.js
··· 17 17 * shift the queue if needed. 18 18 */ 19 19 class QueueAudioOrchestrator extends BroadcastableDiffuseElement { 20 + static NAME = "diffuse/orchestrator/queue-audio"; 20 21 static observedAttributes = ["repeat"]; 21 22 22 23 // SIGNALS ··· 126 127 // The idea is that scrobblers would more easily pick this up, 127 128 // as opposed to just resetting the audio. 128 129 if (this.#repeat.value) { 129 - await this.queue.add({ 130 - inFront: true, 131 - tracks: [this.queue.now()], 132 - }); 130 + const now = this.queue.now(); 131 + if (now) { 132 + await this.queue.add({ 133 + inFront: true, 134 + tracks: [now], 135 + }); 136 + } 133 137 } 134 138 135 139 await this.queue.shift();
+1 -1
src/definitions/index.ts
··· 1 - export * as ShDiffuseOutputTracks from "./types/sh/diffuse/output/tracks.ts"; 1 + export * as ShDiffuseOutputTracks from "./types/sh/diffuse/output/tracks.js";
+4
src/index.vto
··· 34 34 title: "Webamp / Browser" 35 35 desc: > 36 36 Collection browser + search in a retro, win98, look. 37 + - url: "themes/webamp/configurators/input/" 38 + title: "Webamp / Input Configurator" 39 + desc: > 40 + Windows 98 styled input configurator where you can add music sources. 37 41 38 42 # ELEMENTS 39 43
+6 -1
src/themes/webamp/browser/element.js
··· 20 20 21 21 #searchResults = signal(/** @type {Track[]} */ ([])); 22 22 23 - $input = signal(/** @type {InputElement | undefined} */ (undefined)); 23 + $input = signal( 24 + /** @type {InputElement | undefined} */ (undefined), 25 + ); 26 + 24 27 $output = signal( 25 28 /** @type {OutputElement<Track[]> | undefined} */ (undefined), 26 29 ); 30 + 27 31 $queue = signal( 28 32 /** @type {import("@components/engine/queue/element.js").CLASS | undefined} */ (undefined), 29 33 ); 34 + 30 35 $search = signal( 31 36 /** @type {import("@components/processor/search/element.js").CLASS | undefined} */ (undefined), 32 37 );
+130 -15
src/themes/webamp/configurators/input/element.js
··· 1 1 import { DiffuseElement, query, whenElementsDefined } from "@common/element.js"; 2 2 import { signal } from "@common/signal.js"; 3 3 4 + import { buildURI as buildOpenSubsonicURI } from "@components/input/opensubsonic/common.js"; 5 + import { buildURI as buildS3cURI } from "@components/input/s3/common.js"; 6 + 4 7 /** 5 8 * @import {RenderArg} from "@common/element.d.ts" 6 9 * @import {Track} from "@definitions/types.d.ts" 7 10 * @import {InputElement} from "@components/input/types.d.ts" 11 + * @import {OutputElement} from "@components/output/types.d.ts" 12 + * 13 + * @import {Server as OpenSubsonicServer} from "@components/input/opensubsonic/types.d.ts" 14 + * @import {Bucket as S3Bucket} from "@components/input/s3/types.d.ts" 8 15 */ 9 16 10 17 class InputConfig extends DiffuseElement { 11 18 constructor() { 12 19 super(); 20 + this.attachShadow({ mode: "open" }); 21 + } 22 + 23 + // SIGNALS 24 + 25 + $input = signal( 26 + /** @type {InputElement | undefined} */ (undefined), 27 + ); 28 + 29 + $output = signal( 30 + /** @type {OutputElement<Track[]> | undefined} */ (undefined), 31 + ); 13 32 14 - this.attachShadow({ mode: "open" }); 33 + // LIFECYCLE 34 + 35 + /** 36 + * @override 37 + */ 38 + connectedCallback() { 39 + super.connectedCallback(); 40 + 41 + /** @type {InputElement} */ 42 + const input = query(this, "input-selector"); 43 + 44 + /** @type {OutputElement<Track[]>} */ 45 + const output = query(this, "output-selector"); 46 + 47 + this.$input.value = input; 48 + this.$output.value = output; 49 + 50 + // Wait for the elements to be defined before proceeding 51 + whenElementsDefined({ input, output }).then(() => { 52 + // 53 + }); 15 54 } 16 55 17 56 // EVENTS ··· 21 60 */ 22 61 #addOpenSubsonicServer = (event) => { 23 62 event.preventDefault(); 24 - console.log("TODO"); 63 + 64 + const host = this.formElement("opensubsonic-host")?.value; 65 + const tls = this.formElement("opensubsonic-tls")?.value === "true"; 66 + const username = this.formElement("opensubsonic-username")?.value; 67 + const password = this.formElement("opensubsonic-password")?.value; 68 + const apiKey = this.formElement("opensubsonic-apikey")?.value; 69 + 70 + if (!host) { 71 + throw new Error("Missing required `host` input value"); 72 + } 73 + 74 + /** @type {OpenSubsonicServer} */ 75 + const server = { 76 + host, 77 + tls, 78 + username, 79 + password, 80 + apiKey, 81 + }; 82 + 83 + const uri = buildOpenSubsonicURI(server); 84 + return this.addSource(uri); 25 85 }; 26 86 27 87 /** ··· 29 89 */ 30 90 #addS3Bucket = (event) => { 31 91 event.preventDefault(); 32 - console.log("TODO"); 92 + 93 + const accessKey = this.formElement("s3-access-key")?.value; 94 + const bucketName = this.formElement("s3-bucket-name")?.value; 95 + const host = this.formElement("s3-host")?.value ?? "s3.amazonaws.com"; 96 + const path = this.formElement("s3-path")?.value ?? "/"; 97 + const region = this.formElement("s3-region")?.value ?? "us-east-1"; 98 + const secretKey = this.formElement("s3-secret-key")?.value; 99 + 100 + if (!accessKey) throw new Error("Missing required `accessKey` input value"); 101 + if (!bucketName) { 102 + throw new Error("Missing required `bucketName` input value"); 103 + } 104 + if (!secretKey) throw new Error("Missing required `secretKey` input value"); 105 + 106 + /** @type {S3Bucket} */ 107 + const bucket = { 108 + accessKey, 109 + bucketName, 110 + host, 111 + path, 112 + region, 113 + secretKey, 114 + }; 115 + 116 + const uri = buildS3cURI(bucket); 117 + return this.addSource(uri); 33 118 }; 119 + 120 + // 🛠️ 121 + 122 + /** 123 + * @param {string} uri 124 + */ 125 + addSource(uri) { 126 + /** @type {Track} */ 127 + const track = { 128 + $type: "sh.diffuse.output.tracks", 129 + id: crypto.randomUUID(), 130 + kind: "placeholder", 131 + uri, 132 + }; 133 + 134 + const output = this.$output.value; 135 + if (!output) throw new Error("Output isn't ready yet!"); 136 + 137 + output.tracks.save( 138 + [...output.tracks.collection(), track], 139 + ); 140 + } 141 + 142 + /** 143 + * @param {string} id 144 + * @returns {HTMLInputElement | null} 145 + */ 146 + formElement(id) { 147 + return this.root().querySelector(`#${id}`); 148 + } 34 149 35 150 // RENDER 36 151 ··· 188 303 <legend>Bucket details</legend> 189 304 190 305 <div class="field-row"> 191 - <label for="access-key-input">Access Key:*</label> 192 - <input type="text" id="access-key-input" required /> 306 + <label for="s3-access-key">Access Key:*</label> 307 + <input type="text" id="s3-access-key" required /> 193 308 </div> 194 309 195 310 <div class="field-row"> 196 - <label for="secret-key-input">Secret Key:*</label> 197 - <input type="password" id="secret-key-input" required /> 311 + <label for="s3-secret-key">Secret Key:*</label> 312 + <input type="password" id="s3-secret-key" required /> 198 313 </div> 199 314 200 315 <div class="field-row"> 201 - <label for="bucket-name-input">Bucket Name:*</label> 202 - <input type="text" id="bucket-name-input" required /> 316 + <label for="s3-bucket-name">Bucket Name:*</label> 317 + <input type="text" id="s3-bucket-name" required /> 203 318 </div> 204 319 205 320 <div class="field-row"> 206 - <label for="s3-host-input">Host:</label> 321 + <label for="s3-host">Host:</label> 207 322 <input 208 323 type="text" 209 - id="s3-host-input" 324 + id="s3-host" 210 325 placeholder="s3.amazonaws.com" 211 326 /> 212 327 </div> 213 328 214 329 <div class="field-row"> 215 - <label for="region-input">Region:</label> 330 + <label for="s3-region">Region:</label> 216 331 <input 217 332 type="text" 218 - id="region-input" 333 + id="s3-region" 219 334 placeholder="us-east-1" 220 335 /> 221 336 </div> 222 337 223 338 <div class="field-row"> 224 - <label for="path-input">Path:</label> 225 - <input type="text" id="path-input" /> 339 + <label for="s3-path">Path:</label> 340 + <input type="text" id="s3-path" /> 226 341 </div> 227 342 228 343 <p>
+4 -1
src/themes/webamp/configurators/input/index.vto
··· 25 25 </div> 26 26 </div> 27 27 <div class="window-body"> 28 - <dtw-input-config></dtw-input-config> 28 + <dtw-input-config 29 + input-selector="do-input" 30 + output-selector="do-output" 31 + ></dtw-input-config> 29 32 </div> 30 33 </div> 31 34