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

Configure Feed

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

feat: input bundle prelude facet

+116 -119
+8
src/_data/facets.json
··· 28 28 "desc": "Export all data as a JSON snapshot, or restore from a previously exported file." 29 29 }, 30 30 { 31 + "url": "facets/data/input-bundle/index.html", 32 + "title": "Default Input Bundle", 33 + "kind": "prelude", 34 + "category": "Data", 35 + "featured": true, 36 + "desc": "The default setup for audio input sources. Supports these services: HTTPS, Icecast, the local filesystem, OpenSubsonic, and S3-compatible storage." 37 + }, 38 + { 31 39 "url": "facets/data/output-bundle/index.html", 32 40 "title": "Default Output Bundle", 33 41 "kind": "prelude",
+1 -1
src/build.vto
··· 67 67 {{- echo -}} 68 68 import foundation from "common/foundation.js" 69 69 {{ /echo }} 70 + {{ echo -}}await foundation.configurator.input(){{- /echo }} 70 71 {{ echo -}}await foundation.configurator.scrobbles(){{- /echo }} 71 72 72 73 {{ echo -}}await foundation.engine.audio(){{- /echo }} ··· 76 77 77 78 {{ echo -}}await foundation.orchestrator.autoQueue(){{- /echo }} 78 79 {{ echo -}}await foundation.orchestrator.favourites(){{- /echo }} 79 - {{ echo -}}await foundation.orchestrator.input(){{- /echo }} 80 80 {{ echo -}}await foundation.orchestrator.mediaSession(){{- /echo }} 81 81 {{ echo -}}await foundation.orchestrator.output(){{- /echo }} 82 82 {{ echo -}}await foundation.orchestrator.queueAudio(){{- /echo }}
+5 -12
src/common/element.js
··· 175 175 return worker; 176 176 } 177 177 178 - /** */ 178 + /** 179 + * @returns {Record<string, DiffuseElement> | null} 180 + */ 179 181 dependencies() { 180 - return Object.fromEntries( 181 - Array.from(this.children).flatMap((element) => { 182 - if ("identifier" in element === false) { 183 - return []; 184 - } 185 - 186 - const d = /** @type {DiffuseElement} */ (element); 187 - return [[d.localName, d]]; 188 - }), 189 - ); 182 + return null; 190 183 } 191 184 192 185 worker() { ··· 225 218 226 219 let toWorker; 227 220 228 - if (Object.keys(deps).length) { 221 + if (deps) { 229 222 toWorker = 230 223 /** 231 224 * @param {any} msg
+17 -17
src/common/foundation.js
··· 14 14 */ 15 15 const signals = { 16 16 configurator: { 17 + input: signal( 18 + /** @type {import("~/components/configurator/input/element.js").CLASS | null} */ (null), 19 + ), 17 20 scrobbles: signal( 18 21 /** @type {import("~/components/configurator/scrobbles/element.js").CLASS | null} */ (null), 19 22 ), ··· 41 44 favourites: signal( 42 45 /** @type {import("~/components/orchestrator/favourites/element.js").CLASS | null} */ (null), 43 46 ), 44 - input: signal( 45 - /** @type {import("~/components/orchestrator/input/element.js").CLASS | null} */ (null), 46 - ), 47 47 mediaSession: signal( 48 48 /** @type {import("~/components/orchestrator/media-session/element.js").CLASS | null} */ (null), 49 49 ), ··· 88 88 89 89 // Elements 90 90 configurator: { 91 + input, 91 92 scrobbles, 92 93 }, 93 94 ··· 101 102 orchestrator: { 102 103 autoQueue, 103 104 favourites, 104 - input, 105 105 mediaSession, 106 106 output, 107 107 queueAudio, ··· 123 123 */ 124 124 signals: { 125 125 configurator: { 126 + input: signals.configurator.input.get, 126 127 scrobbles: signals.configurator.scrobbles.get, 127 128 }, 128 129 ··· 136 137 orchestrator: { 137 138 autoQueue: signals.orchestrator.autoQueue.get, 138 139 favourites: signals.orchestrator.favourites.get, 139 - input: signals.orchestrator.input.get, 140 140 mediaSession: signals.orchestrator.mediaSession.get, 141 141 output: signals.orchestrator.output.get, 142 142 processTracks: signals.orchestrator.processTracks.get, ··· 160 160 161 161 // Configurators 162 162 163 + async function input() { 164 + const { default: InputConfigurator } = await import( 165 + "~/components/configurator/input/element.js" 166 + ); 167 + 168 + const i = new InputConfigurator(); 169 + i.setAttribute("group", GROUP); 170 + i.setAttribute("id", "input"); 171 + 172 + return findExistingOrAdd(i, signals.configurator.input); 173 + } 174 + 163 175 /** 164 176 * @returns {Promise<ScrobbleElement>} 165 177 */ ··· 297 309 fo.setAttribute("output-selector", o.selector); 298 310 299 311 return findExistingOrAdd(fo, signals.orchestrator.favourites); 300 - } 301 - 302 - async function input() { 303 - const { default: InputOrchestrator } = await import( 304 - "~/components/orchestrator/input/element.js" 305 - ); 306 - 307 - const i = new InputOrchestrator(); 308 - i.setAttribute("group", GROUP); 309 - i.setAttribute("id", "input"); 310 - 311 - return findExistingOrAdd(i, signals.orchestrator.input); 312 312 } 313 313 314 314 async function mediaSession() {
+1 -1
src/common/pages/guide.js
··· 17 17 const { default: foundation } = await import("~/common/foundation.js"); 18 18 const Output = await import("~/common/output.js"); 19 19 20 + const input = await foundation.configurator.input(); 20 21 const output = await foundation.orchestrator.output(); 21 - const input = await foundation.orchestrator.input(); 22 22 const pto = await foundation.orchestrator.processTracks({ 23 23 disableWhenReady: true, 24 24 });
-85
src/components/orchestrator/input/element.js
··· 1 - import { DiffuseElement } from "~/common/element.js"; 2 - 3 - import "~/components/configurator/input/element.js"; 4 - import "~/components/input/https/element.js"; 5 - import "~/components/input/icecast/element.js"; 6 - import "~/components/input/local/element.js"; 7 - import "~/components/input/opensubsonic/element.js"; 8 - import "~/components/input/s3/element.js"; 9 - 10 - /** 11 - * @import {RenderArg} from "~/common/element.d.ts" 12 - * @import {InputActions, InputElement} from "~/components/input/types.d.ts" 13 - */ 14 - 15 - //////////////////////////////////////////// 16 - // ELEMENT 17 - //////////////////////////////////////////// 18 - 19 - class InputOrchestrator extends DiffuseElement { 20 - static NAME = "diffuse/orchestrator/input"; 21 - static WORKER_URL = "components/configurator/input/worker.js"; 22 - 23 - /** 24 - * @returns {InputElement} 25 - */ 26 - get input() { 27 - /** @type {InputElement | null} */ 28 - const input = this.querySelector("dc-input"); 29 - 30 - if (!input) throw new Error("Input orchestrator did not render yet."); 31 - return input; 32 - } 33 - 34 - // PROXY INPUT ACTIONS 35 - 36 - consult = /** @type {InputActions["consult"]} */ (...args) => 37 - this.input.consult(...args); 38 - 39 - detach = /** @type {InputActions["detach"]} */ (...args) => 40 - this.input.detach(...args); 41 - 42 - groupConsult = /** @type {InputActions["groupConsult"]} */ (...args) => 43 - this.input.groupConsult(...args); 44 - 45 - list = /** @type {InputActions["list"]} */ (...args) => 46 - this.input.list(...args); 47 - 48 - resolve = /** @type {InputActions["resolve"]} */ (...args) => 49 - this.input.resolve(...args); 50 - 51 - // PROXY OTHER FUNCTIONS 52 - 53 - /** @override */ 54 - dependencies() { 55 - return this.input.dependencies(); 56 - } 57 - 58 - // RENDER 59 - 60 - /** 61 - * @param {RenderArg} _ 62 - */ 63 - render({ html }) { 64 - return html` 65 - <dc-input> 66 - <di-https></di-https> 67 - <di-icecast></di-icecast> 68 - <di-local></di-local> 69 - <di-opensubsonic></di-opensubsonic> 70 - <di-s3></di-s3> 71 - </dc-input> 72 - `; 73 - } 74 - } 75 - 76 - export default InputOrchestrator; 77 - 78 - //////////////////////////////////////////// 79 - // REGISTER 80 - //////////////////////////////////////////// 81 - 82 - export const CLASS = InputOrchestrator; 83 - export const NAME = "do-input"; 84 - 85 - customElements.define(NAME, CLASS);
+81
src/facets/data/input-bundle/index.inline.js
··· 1 + import foundation from "~/common/foundation.js"; 2 + import { effect } from "~/common/signal.js"; 3 + 4 + import { NAME as HTTPS_NAME } from "~/components/input/https/element.js"; 5 + import { NAME as ICECAST_NAME } from "~/components/input/icecast/element.js"; 6 + import { NAME as LOCAL_NAME } from "~/components/input/local/element.js"; 7 + import { NAME as OPENSUBSONIC_NAME } from "~/components/input/opensubsonic/element.js"; 8 + import { NAME as S3_NAME } from "~/components/input/s3/element.js"; 9 + 10 + /** 11 + * @import InputConfigurator from "~/components/configurator/input/element.js" 12 + */ 13 + 14 + /** 15 + * Setup DOM elements when needed. 16 + */ 17 + effect(() => { 18 + const input = foundation.signals.configurator.input(); 19 + if (!input) return; 20 + 21 + https(input); 22 + icecast(input); 23 + local(input); 24 + opensubsonic(input); 25 + s3(input); 26 + }); 27 + 28 + //////////////////////////////////////////// 29 + // HTTPS 30 + //////////////////////////////////////////// 31 + 32 + /** 33 + * @param {InputConfigurator} input 34 + */ 35 + export function https(input) { 36 + input.append(document.createElement(HTTPS_NAME)); 37 + } 38 + 39 + //////////////////////////////////////////// 40 + // ICECAST 41 + //////////////////////////////////////////// 42 + 43 + /** 44 + * @param {InputConfigurator} input 45 + */ 46 + export function icecast(input) { 47 + input.append(document.createElement(ICECAST_NAME)); 48 + } 49 + 50 + //////////////////////////////////////////// 51 + // LOCAL 52 + //////////////////////////////////////////// 53 + 54 + /** 55 + * @param {InputConfigurator} input 56 + */ 57 + export function local(input) { 58 + input.append(document.createElement(LOCAL_NAME)); 59 + } 60 + 61 + //////////////////////////////////////////// 62 + // OPENSUBSONIC 63 + //////////////////////////////////////////// 64 + 65 + /** 66 + * @param {InputConfigurator} input 67 + */ 68 + export function opensubsonic(input) { 69 + input.append(document.createElement(OPENSUBSONIC_NAME)); 70 + } 71 + 72 + //////////////////////////////////////////// 73 + // S3 74 + //////////////////////////////////////////// 75 + 76 + /** 77 + * @param {InputConfigurator} input 78 + */ 79 + export function s3(input) { 80 + input.append(document.createElement(S3_NAME)); 81 + }
+1 -1
src/themes/blur/artwork-controller/facet/index.inline.js
··· 9 9 foundation.engine.audio(), 10 10 foundation.processor.artwork(), 11 11 foundation.orchestrator.favourites(), 12 - foundation.orchestrator.input(), 12 + foundation.configurator.input(), 13 13 foundation.orchestrator.output(), 14 14 foundation.engine.queue(), 15 15 ]);
+1 -1
src/themes/winamp/configurators/input/facet/index.inline.js
··· 2 2 import InputConfigElement from "~/themes/winamp/configurators/input/element.js"; 3 3 4 4 const [inp, out, pro, sou] = await Promise.all([ 5 - foundation.orchestrator.input(), 5 + foundation.configurator.input(), 6 6 foundation.orchestrator.output(), 7 7 foundation.orchestrator.processTracks({ disableWhenReady: true }), 8 8 foundation.orchestrator.sources(),
+1 -1
src/themes/winamp/facet/index.inline.js
··· 9 9 * @import {Track} from "~/definitions/types.d.ts" 10 10 */ 11 11 12 - const input = await foundation.orchestrator.input(); 12 + const input = await foundation.configurator.input(); 13 13 const queue = await foundation.engine.queue(); 14 14 const scopedTracks = await foundation.orchestrator.scopedTracks(); 15 15