diff --git a/src/_data/facets.json b/src/_data/facets.json index 7d2cf1c5..c527175b 100644 --- a/src/_data/facets.json +++ b/src/_data/facets.json @@ -27,6 +27,14 @@ "category": "Data", "desc": "Export all data as a JSON snapshot, or restore from a previously exported file." }, + { + "url": "facets/data/input-bundle/index.html", + "title": "Default Input Bundle", + "kind": "prelude", + "category": "Data", + "featured": true, + "desc": "The default setup for audio input sources. Supports these services: HTTPS, Icecast, the local filesystem, OpenSubsonic, and S3-compatible storage." + }, { "url": "facets/data/output-bundle/index.html", "title": "Default Output Bundle", diff --git a/src/build.vto b/src/build.vto index 3a3fecd7..5f6bf1a4 100644 --- a/src/build.vto +++ b/src/build.vto @@ -67,6 +67,7 @@ examples: {{- echo -}} import foundation from "common/foundation.js" {{ /echo }} +{{ echo -}}await foundation.configurator.input(){{- /echo }} {{ echo -}}await foundation.configurator.scrobbles(){{- /echo }} {{ echo -}}await foundation.engine.audio(){{- /echo }} @@ -76,7 +77,6 @@ examples: {{ echo -}}await foundation.orchestrator.autoQueue(){{- /echo }} {{ echo -}}await foundation.orchestrator.favourites(){{- /echo }} -{{ echo -}}await foundation.orchestrator.input(){{- /echo }} {{ echo -}}await foundation.orchestrator.mediaSession(){{- /echo }} {{ echo -}}await foundation.orchestrator.output(){{- /echo }} {{ echo -}}await foundation.orchestrator.queueAudio(){{- /echo }} diff --git a/src/common/element.js b/src/common/element.js index ece0331f..dda96a5a 100644 --- a/src/common/element.js +++ b/src/common/element.js @@ -175,18 +175,11 @@ export class DiffuseElement extends HTMLElement { return worker; } - /** */ + /** + * @returns {Record | null} + */ dependencies() { - return Object.fromEntries( - Array.from(this.children).flatMap((element) => { - if ("identifier" in element === false) { - return []; - } - - const d = /** @type {DiffuseElement} */ (element); - return [[d.localName, d]]; - }), - ); + return null; } worker() { @@ -225,7 +218,7 @@ export class DiffuseElement extends HTMLElement { let toWorker; - if (Object.keys(deps).length) { + if (deps) { toWorker = /** * @param {any} msg diff --git a/src/common/foundation.js b/src/common/foundation.js index 34367834..ec5b3a9a 100644 --- a/src/common/foundation.js +++ b/src/common/foundation.js @@ -14,6 +14,9 @@ export const GROUP = url.searchParams.get("group") ?? "facets"; */ const signals = { configurator: { + input: signal( + /** @type {import("~/components/configurator/input/element.js").CLASS | null} */ (null), + ), scrobbles: signal( /** @type {import("~/components/configurator/scrobbles/element.js").CLASS | null} */ (null), ), @@ -41,9 +44,6 @@ const signals = { favourites: signal( /** @type {import("~/components/orchestrator/favourites/element.js").CLASS | null} */ (null), ), - input: signal( - /** @type {import("~/components/orchestrator/input/element.js").CLASS | null} */ (null), - ), mediaSession: signal( /** @type {import("~/components/orchestrator/media-session/element.js").CLASS | null} */ (null), ), @@ -88,6 +88,7 @@ export const config = { // Elements configurator: { + input, scrobbles, }, @@ -101,7 +102,6 @@ export const config = { orchestrator: { autoQueue, favourites, - input, mediaSession, output, queueAudio, @@ -123,6 +123,7 @@ export const config = { */ signals: { configurator: { + input: signals.configurator.input.get, scrobbles: signals.configurator.scrobbles.get, }, @@ -136,7 +137,6 @@ export const config = { orchestrator: { autoQueue: signals.orchestrator.autoQueue.get, favourites: signals.orchestrator.favourites.get, - input: signals.orchestrator.input.get, mediaSession: signals.orchestrator.mediaSession.get, output: signals.orchestrator.output.get, processTracks: signals.orchestrator.processTracks.get, @@ -160,6 +160,18 @@ export default config; // Configurators +async function input() { + const { default: InputConfigurator } = await import( + "~/components/configurator/input/element.js" + ); + + const i = new InputConfigurator(); + i.setAttribute("group", GROUP); + i.setAttribute("id", "input"); + + return findExistingOrAdd(i, signals.configurator.input); +} + /** * @returns {Promise} */ @@ -299,18 +311,6 @@ async function favourites() { return findExistingOrAdd(fo, signals.orchestrator.favourites); } -async function input() { - const { default: InputOrchestrator } = await import( - "~/components/orchestrator/input/element.js" - ); - - const i = new InputOrchestrator(); - i.setAttribute("group", GROUP); - i.setAttribute("id", "input"); - - return findExistingOrAdd(i, signals.orchestrator.input); -} - async function mediaSession() { const [{ default: MediaSessionOrchestrator }, a, aw, o, q] = await Promise .all([ diff --git a/src/common/pages/guide.js b/src/common/pages/guide.js index 5d41e4da..cb96b2fd 100644 --- a/src/common/pages/guide.js +++ b/src/common/pages/guide.js @@ -17,8 +17,8 @@ async function addSampleContent() { const { default: foundation } = await import("~/common/foundation.js"); const Output = await import("~/common/output.js"); + const input = await foundation.configurator.input(); const output = await foundation.orchestrator.output(); - const input = await foundation.orchestrator.input(); const pto = await foundation.orchestrator.processTracks({ disableWhenReady: true, }); diff --git a/src/components/orchestrator/input/element.js b/src/components/orchestrator/input/element.js deleted file mode 100644 index af2d7f02..00000000 --- a/src/components/orchestrator/input/element.js +++ /dev/null @@ -1,85 +0,0 @@ -import { DiffuseElement } from "~/common/element.js"; - -import "~/components/configurator/input/element.js"; -import "~/components/input/https/element.js"; -import "~/components/input/icecast/element.js"; -import "~/components/input/local/element.js"; -import "~/components/input/opensubsonic/element.js"; -import "~/components/input/s3/element.js"; - -/** - * @import {RenderArg} from "~/common/element.d.ts" - * @import {InputActions, InputElement} from "~/components/input/types.d.ts" - */ - -//////////////////////////////////////////// -// ELEMENT -//////////////////////////////////////////// - -class InputOrchestrator extends DiffuseElement { - static NAME = "diffuse/orchestrator/input"; - static WORKER_URL = "components/configurator/input/worker.js"; - - /** - * @returns {InputElement} - */ - get input() { - /** @type {InputElement | null} */ - const input = this.querySelector("dc-input"); - - if (!input) throw new Error("Input orchestrator did not render yet."); - return input; - } - - // PROXY INPUT ACTIONS - - consult = /** @type {InputActions["consult"]} */ (...args) => - this.input.consult(...args); - - detach = /** @type {InputActions["detach"]} */ (...args) => - this.input.detach(...args); - - groupConsult = /** @type {InputActions["groupConsult"]} */ (...args) => - this.input.groupConsult(...args); - - list = /** @type {InputActions["list"]} */ (...args) => - this.input.list(...args); - - resolve = /** @type {InputActions["resolve"]} */ (...args) => - this.input.resolve(...args); - - // PROXY OTHER FUNCTIONS - - /** @override */ - dependencies() { - return this.input.dependencies(); - } - - // RENDER - - /** - * @param {RenderArg} _ - */ - render({ html }) { - return html` - - - - - - - - `; - } -} - -export default InputOrchestrator; - -//////////////////////////////////////////// -// REGISTER -//////////////////////////////////////////// - -export const CLASS = InputOrchestrator; -export const NAME = "do-input"; - -customElements.define(NAME, CLASS); diff --git a/src/facets/data/input-bundle/index.inline.js b/src/facets/data/input-bundle/index.inline.js index e69de29b..ede910d2 100644 --- a/src/facets/data/input-bundle/index.inline.js +++ b/src/facets/data/input-bundle/index.inline.js @@ -0,0 +1,81 @@ +import foundation from "~/common/foundation.js"; +import { effect } from "~/common/signal.js"; + +import { NAME as HTTPS_NAME } from "~/components/input/https/element.js"; +import { NAME as ICECAST_NAME } from "~/components/input/icecast/element.js"; +import { NAME as LOCAL_NAME } from "~/components/input/local/element.js"; +import { NAME as OPENSUBSONIC_NAME } from "~/components/input/opensubsonic/element.js"; +import { NAME as S3_NAME } from "~/components/input/s3/element.js"; + +/** + * @import InputConfigurator from "~/components/configurator/input/element.js" + */ + +/** + * Setup DOM elements when needed. + */ +effect(() => { + const input = foundation.signals.configurator.input(); + if (!input) return; + + https(input); + icecast(input); + local(input); + opensubsonic(input); + s3(input); +}); + +//////////////////////////////////////////// +// HTTPS +//////////////////////////////////////////// + +/** + * @param {InputConfigurator} input + */ +export function https(input) { + input.append(document.createElement(HTTPS_NAME)); +} + +//////////////////////////////////////////// +// ICECAST +//////////////////////////////////////////// + +/** + * @param {InputConfigurator} input + */ +export function icecast(input) { + input.append(document.createElement(ICECAST_NAME)); +} + +//////////////////////////////////////////// +// LOCAL +//////////////////////////////////////////// + +/** + * @param {InputConfigurator} input + */ +export function local(input) { + input.append(document.createElement(LOCAL_NAME)); +} + +//////////////////////////////////////////// +// OPENSUBSONIC +//////////////////////////////////////////// + +/** + * @param {InputConfigurator} input + */ +export function opensubsonic(input) { + input.append(document.createElement(OPENSUBSONIC_NAME)); +} + +//////////////////////////////////////////// +// S3 +//////////////////////////////////////////// + +/** + * @param {InputConfigurator} input + */ +export function s3(input) { + input.append(document.createElement(S3_NAME)); +} diff --git a/src/themes/blur/artwork-controller/facet/index.inline.js b/src/themes/blur/artwork-controller/facet/index.inline.js index c9c83e6d..8fd89ba3 100644 --- a/src/themes/blur/artwork-controller/facet/index.inline.js +++ b/src/themes/blur/artwork-controller/facet/index.inline.js @@ -9,7 +9,7 @@ const [aud, art, fav, inp, out, que] = await Promise.all([ foundation.engine.audio(), foundation.processor.artwork(), foundation.orchestrator.favourites(), - foundation.orchestrator.input(), + foundation.configurator.input(), foundation.orchestrator.output(), foundation.engine.queue(), ]); diff --git a/src/themes/winamp/configurators/input/facet/index.inline.js b/src/themes/winamp/configurators/input/facet/index.inline.js index 4b16ffd3..c4e1d9d6 100644 --- a/src/themes/winamp/configurators/input/facet/index.inline.js +++ b/src/themes/winamp/configurators/input/facet/index.inline.js @@ -2,7 +2,7 @@ import foundation from "~/common/foundation.js"; import InputConfigElement from "~/themes/winamp/configurators/input/element.js"; const [inp, out, pro, sou] = await Promise.all([ - foundation.orchestrator.input(), + foundation.configurator.input(), foundation.orchestrator.output(), foundation.orchestrator.processTracks({ disableWhenReady: true }), foundation.orchestrator.sources(), diff --git a/src/themes/winamp/facet/index.inline.js b/src/themes/winamp/facet/index.inline.js index 5804600e..fc6c1e60 100644 --- a/src/themes/winamp/facet/index.inline.js +++ b/src/themes/winamp/facet/index.inline.js @@ -9,7 +9,7 @@ import WebampElement from "~/themes/winamp/webamp/element.js"; * @import {Track} from "~/definitions/types.d.ts" */ -const input = await foundation.orchestrator.input(); +const input = await foundation.configurator.input(); const queue = await foundation.engine.queue(); const scopedTracks = await foundation.orchestrator.scopedTracks();