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: scoped-tracks orchestrator

+315 -256
+4 -4
deno.jsonc
··· 81 81 "./components/engine/queue/element.js": "./src/components/engine/queue/element.js", 82 82 "./components/engine/queue/worker.js": "./src/components/engine/queue/worker.js", 83 83 "./components/engine/repeat-shuffle/element.js": "./src/components/engine/repeat-shuffle/element.js", 84 + "./components/engine/scope/element.js": "./src/components/engine/scope/element.js", 84 85 "./components/input/common.js": "./src/components/input/common.js", 85 86 "./components/input/opensubsonic/common.js": "./src/components/input/opensubsonic/common.js", 86 87 "./components/input/opensubsonic/constants.js": "./src/components/input/opensubsonic/constants.js", ··· 91 92 "./components/input/s3/element.js": "./src/components/input/s3/element.js", 92 93 "./components/input/s3/worker.js": "./src/components/input/s3/worker.js", 93 94 "./components/orchestrator/auto-queue/element.js": "./src/components/orchestrator/auto-queue/element.js", 94 - "./components/orchestrator/auto-queue/worker.js": "./src/components/orchestrator/auto-queue/worker.js", 95 95 "./components/orchestrator/input/element.js": "./src/components/orchestrator/input/element.js", 96 96 "./components/orchestrator/output/element.js": "./src/components/orchestrator/output/element.js", 97 97 "./components/orchestrator/process-tracks/element.js": "./src/components/orchestrator/process-tracks/element.js", 98 98 "./components/orchestrator/process-tracks/worker.js": "./src/components/orchestrator/process-tracks/worker.js", 99 99 "./components/orchestrator/queue-audio/element.js": "./src/components/orchestrator/queue-audio/element.js", 100 - "./components/orchestrator/search-tracks/element.js": "./src/components/orchestrator/search-tracks/element.js", 101 - "./components/orchestrator/search-tracks/worker.js": "./src/components/orchestrator/search-tracks/worker.js", 100 + "./components/orchestrator/scoped-tracks/element.js": "./src/components/orchestrator/scoped-tracks/element.js", 101 + "./components/orchestrator/scoped-tracks/worker.js": "./src/components/orchestrator/scoped-tracks/worker.js", 102 102 "./components/orchestrator/sources/element.js": "./src/components/orchestrator/sources/element.js", 103 103 "./components/output/common.js": "./src/components/output/common.js", 104 104 "./components/output/polymorphic/indexed-db/constants.js": "./src/components/output/polymorphic/indexed-db/constants.js", ··· 128 128 "./components/input/types.d.ts": "./src/components/input/types.d.ts", 129 129 "./components/orchestrator/auto-queue/types.d.ts": "./src/components/orchestrator/auto-queue/types.d.ts", 130 130 "./components/orchestrator/process-tracks/types.d.ts": "./src/components/orchestrator/process-tracks/types.d.ts", 131 - "./components/orchestrator/search-tracks/types.d.ts": "./src/components/orchestrator/search-tracks/types.d.ts", 131 + "./components/orchestrator/scoped-tracks/types.d.ts": "./src/components/orchestrator/scoped-tracks/types.d.ts", 132 132 "./components/output/polymorphic/indexed-db/types.d.ts": "./src/components/output/polymorphic/indexed-db/types.d.ts", 133 133 "./components/output/types.d.ts": "./src/components/output/types.d.ts", 134 134 "./components/processor/artwork/types.d.ts": "./src/components/processor/artwork/types.d.ts",
+35 -21
src/common/constituents/foundation.js
··· 9 9 import QueueAudioOrchestrator from "@components/orchestrator/queue-audio/element.js"; 10 10 import RepeatShuffleEngine from "@components/engine/repeat-shuffle/element.js"; 11 11 import SearchProcessor from "@components/processor/search/element.js"; 12 - import SearchTracksOrchestrator from "@components/orchestrator/search-tracks/element.js"; 12 + import ScopeEngine from "@components/engine/scope/element.js"; 13 + import ScopedTracksOrchestrator from "@components/orchestrator/scoped-tracks/element.js"; 13 14 import SourcesOrchestrator from "@components/orchestrator/sources/element.js"; 14 15 15 16 /** ··· 36 37 audio, 37 38 queue, 38 39 repeatShuffle, 40 + scope, 39 41 }, 40 42 orchestrator: { 41 43 autoQueue, ··· 43 45 output, 44 46 queueAudio, 45 47 processTracks, 46 - searchTracks, 48 + scopedTracks, 47 49 sources, 48 50 }, 49 51 processor: { ··· 62 64 engine: { 63 65 queue: queue(), 64 66 repeatShuffle: repeatShuffle(), 67 + scope: scope(), 65 68 }, 66 69 orchestrator: { 67 70 autoQueue: autoQueue(), 68 71 input: input(), 69 72 output: output(), 73 + scopedTracks: scopedTracks(), 70 74 }, 71 75 }; 72 76 } ··· 98 102 99 103 function searchThroughCollection() { 100 104 return { 105 + engine: { 106 + scope: scope(), 107 + }, 101 108 orchestrator: { 102 109 output: output(), 103 - searchTracks: searchTracks(), 110 + scopedTracks: scopedTracks(), 104 111 }, 105 112 processor: { 106 113 search: search(), ··· 125 132 return findExistingOrAdd(q); 126 133 } 127 134 135 + function repeatShuffle() { 136 + const r = new RepeatShuffleEngine(); 137 + r.setAttribute("group", GROUP); 138 + 139 + return findExistingOrAdd(r); 140 + } 141 + 142 + function scope() { 143 + const s = new ScopeEngine(); 144 + s.setAttribute("group", GROUP); 145 + 146 + return findExistingOrAdd(s); 147 + } 148 + 128 149 // Processors 129 150 function artwork() { 130 151 const a = new ArtworkProcessor(); ··· 149 170 150 171 // Orchestrators 151 172 function autoQueue() { 152 - const i = input(); 153 - const o = output(); 154 173 const q = queue(); 155 174 const r = repeatShuffle(); 175 + const t = scopedTracks(); 156 176 157 177 const aqo = new AutoQueueOrchestrator(); 158 178 aqo.setAttribute("group", GROUP); 159 - aqo.setAttribute("input-selector", i.selector); 160 - aqo.setAttribute("output-selector", o.selector); 161 179 aqo.setAttribute("queue-engine-selector", q.selector); 162 180 aqo.setAttribute("repeat-shuffle-engine-selector", r.selector); 181 + aqo.setAttribute("tracks-selector", t.selector); 163 182 164 183 return findExistingOrAdd(aqo); 165 184 } ··· 211 230 return findExistingOrAdd(oqa); 212 231 } 213 232 214 - function repeatShuffle() { 215 - const rse = new RepeatShuffleEngine(); 216 - rse.setAttribute("group", GROUP); 217 - 218 - return findExistingOrAdd(rse); 219 - } 220 - 221 - function searchTracks() { 233 + function scopedTracks() { 222 234 const i = input(); 223 235 const o = output(); 236 + const e = scope(); 224 237 const s = search(); 225 238 226 - const ost = new SearchTracksOrchestrator(); 227 - ost.setAttribute("group", GROUP); 228 - ost.setAttribute("input-selector", i.selector); 229 - ost.setAttribute("output-selector", o.selector); 230 - ost.setAttribute("search-processor-selector", s.selector); 239 + const sto = new ScopedTracksOrchestrator(); 240 + sto.setAttribute("group", GROUP); 241 + sto.setAttribute("input-selector", i.selector); 242 + sto.setAttribute("output-selector", o.selector); 243 + sto.setAttribute("scope-engine-selector", e.selector); 244 + sto.setAttribute("search-processor-selector", s.selector); 231 245 232 - return findExistingOrAdd(ost); 246 + return findExistingOrAdd(sto); 233 247 } 234 248 235 249 function sources() {
+17 -1
src/common/element.js
··· 518 518 /** @type {T | null} */ 519 519 const element = document.querySelector(selector); 520 520 if (!element) throw new Error(`Missing required '${selector}' element`); 521 + return element; 522 + } 521 523 522 - return element; 524 + /** 525 + * @template {HTMLElement} T 526 + * @param {DiffuseElement} parent 527 + * @param {string} attribute 528 + */ 529 + export function queryOptional(parent, attribute) { 530 + const selector = parent.getAttribute(attribute); 531 + 532 + if (!selector) { 533 + return null; 534 + } 535 + 536 + /** @type {T | null} */ 537 + const elementOrNull = document.querySelector(selector); 538 + return elementOrNull; 523 539 } 524 540 525 541 /**
+86
src/components/engine/scope/element.js
··· 1 + import { BroadcastableDiffuseElement } from "@common/element.js"; 2 + import { signal } from "@common/signal.js"; 3 + 4 + //////////////////////////////////////////// 5 + // ELEMENT 6 + //////////////////////////////////////////// 7 + 8 + class ScopeEngine extends BroadcastableDiffuseElement { 9 + static NAME = "diffuse/engine/scope"; 10 + 11 + // SIGNALS 12 + 13 + #playlistId = signal(/** @type {string | undefined} */ (undefined)); 14 + #searchTerm = signal(/** @type {string | undefined} */ (undefined)); 15 + 16 + playlistId = this.#playlistId.get; 17 + searchTerm = this.#searchTerm.get; 18 + 19 + // LIFECYCLE 20 + 21 + /** 22 + * @override 23 + */ 24 + connectedCallback() { 25 + // Broadcast if needed 26 + if (this.hasAttribute("group")) { 27 + const actions = this.broadcast(this.nameWithGroup, { 28 + setPlaylistId: { strategy: "replicate", fn: this.setPlaylistId }, 29 + setSearchTerm: { strategy: "replicate", fn: this.setSearchTerm }, 30 + }); 31 + 32 + if (actions) { 33 + this.setPlaylistId = actions.setPlaylistId; 34 + this.setSearchTerm = actions.setSearchTerm; 35 + } 36 + } 37 + 38 + // Super 39 + super.connectedCallback(); 40 + 41 + // Signals 42 + const storagePrefix = 43 + `${this.constructor.prototype.constructor.NAME}/${this.group}/`; 44 + 45 + this.#playlistId.value = 46 + localStorage.getItem(`${storagePrefix}/playlistId`) ?? undefined; 47 + this.#searchTerm.value = 48 + localStorage.getItem(`${storagePrefix}/searchTerm`) ?? undefined; 49 + 50 + // Effects 51 + this.effect(() => { 52 + const key = `${storagePrefix}/playlistId`; 53 + const val = this.#playlistId.value; 54 + 55 + if (val) localStorage.setItem(key, val); 56 + else localStorage.removeItem(key); 57 + }); 58 + 59 + this.effect(() => { 60 + const key = `${storagePrefix}/searchTerm`; 61 + const val = this.#searchTerm.value; 62 + 63 + if (val) localStorage.setItem(key, val); 64 + else localStorage.removeItem(key); 65 + }); 66 + } 67 + 68 + // ACTIONS 69 + 70 + /** @param {string | undefined} val */ 71 + setPlaylistId = async (val) => this.#playlistId.value = val; 72 + 73 + /** @param {string | undefined} val */ 74 + setSearchTerm = async (val) => this.#searchTerm.value = val; 75 + } 76 + 77 + export default ScopeEngine; 78 + 79 + //////////////////////////////////////////// 80 + // REGISTER 81 + //////////////////////////////////////////// 82 + 83 + export const CLASS = ScopeEngine; 84 + export const NAME = "de-scope"; 85 + 86 + customElements.define(NAME, CLASS);
+9 -54
src/components/orchestrator/auto-queue/element.js
··· 1 1 import { BroadcastableDiffuseElement, query } from "@common/element.js"; 2 - import { untracked } from "@common/signal.js"; 3 2 4 3 /** 5 - * @import {ProxiedActions} from "@common/worker.d.ts" 6 - * @import {InputElement} from "@components/input/types.d.ts" 7 - * @import {OutputElement} from "@components/output/types.d.ts" 4 + * @import {DiffuseElement} from "@common/element.js"; 5 + * @import {SignalReader} from "@common/signal.d.ts"; 6 + * @import {Track} from "@definitions/types.d.ts" 8 7 * @import RepeatShuffleEngine from "@components/engine/repeat-shuffle/element.js" 9 - * 10 - * @import {Actions} from "./types.d.ts" 11 8 */ 12 9 13 10 //////////////////////////////////////////// ··· 17 14 /** 18 15 * Update the queue pool whenever tracks have been loaded, 19 16 * or the tracks collection changes. 20 - * 21 - * At the same time, 22 17 */ 23 18 class AutoTracksOrchestrator extends BroadcastableDiffuseElement { 24 19 static NAME = "diffuse/orchestrator/auto-queue"; 25 - static WORKER_URL = "components/orchestrator/auto-queue/worker.js"; 26 - 27 - /** @type {ProxiedActions<Actions>} */ 28 - #proxy; 29 - 30 - constructor() { 31 - super(); 32 - this.#proxy = this.workerProxy({ 33 - forceNew: { 34 - dependencies: { 35 - input: true, 36 - }, 37 - }, 38 - }); 39 - } 40 20 41 21 // LIFECYCLE 42 22 ··· 51 31 52 32 // Super 53 33 super.connectedCallback(); 54 - 55 - /** @type {InputElement} */ 56 - const input = query(this, "input-selector"); 57 - 58 - /** @type {OutputElement} */ 59 - const output = query(this, "output-selector"); 60 34 61 35 /** @type {import("@components/engine/queue/element.js").CLASS} */ 62 36 const queue = query(this, "queue-engine-selector"); ··· 64 38 /** @type {RepeatShuffleEngine} */ 65 39 const repeatShuffle = query(this, "repeat-shuffle-engine-selector"); 66 40 67 - // Assign to self 68 - this.input = input; 69 - this.output = output; 70 - this.queue = queue; 71 - this.repeatShuffle = repeatShuffle; 41 + /** @type {DiffuseElement & { tracks: SignalReader<Track[]> }} */ 42 + const tracksElement = query(this, "tracks-selector"); 72 43 73 44 // When defined 74 - await customElements.whenDefined(input.localName); 75 - await customElements.whenDefined(output.localName); 76 45 await customElements.whenDefined(queue.localName); 77 46 await customElements.whenDefined(repeatShuffle.localName); 47 + await customElements.whenDefined(tracksElement.localName); 78 48 79 - // Watch tracks collection 49 + // Watch tracks 80 50 this.effect(() => { 81 - const tracks = output.tracks.collection(); 51 + const tracks = tracksElement.tracks(); 82 52 83 53 this.isLeader().then((isLeader) => { 84 54 if (!isLeader) return; 85 - untracked(() => this.#proxy.poolAvailable({ tracks })); 55 + queue.supply({ tracks }); 86 56 }); 87 57 }); 88 58 ··· 110 80 if (!trigger) queue.shift(); 111 81 }); 112 82 }); 113 - } 114 - 115 - // WORKERS 116 - 117 - /** 118 - * @override 119 - */ 120 - dependencies() { 121 - if (!this.input) throw new Error("Input element not defined yet"); 122 - if (!this.queue) throw new Error("Queue element not defined yet"); 123 - 124 - return { 125 - input: this.input, 126 - queue: this.queue, 127 - }; 128 83 } 129 84 } 130 85
-5
src/components/orchestrator/auto-queue/types.d.ts
··· 1 - import type { Track } from "@definitions/types.d.ts"; 2 - 3 - export type Actions = { 4 - poolAvailable(_: { tracks: Track[] }): Promise<void>; 5 - };
-51
src/components/orchestrator/auto-queue/worker.js
··· 1 - import { ostiary, rpc, workerProxy } from "@common/worker.js"; 2 - 3 - /** 4 - * @import {Track} from "@definitions/types.d.ts" 5 - * @import {ActionsWithTunnel, ProxiedActions} from "@common/worker.d.ts" 6 - * @import {InputActions} from "@components/input/types.d.ts" 7 - * @import {Actions as QueueEngineActions} from "@components/engine/queue/types.d.ts" 8 - * @import {Actions} from "./types.d.ts" 9 - */ 10 - 11 - //////////////////////////////////////////// 12 - // ACTIONS 13 - //////////////////////////////////////////// 14 - 15 - /** 16 - * @type {ActionsWithTunnel<Actions>["poolAvailable"]} 17 - */ 18 - export async function poolAvailable({ data, ports }) { 19 - const cachedTracks = data.tracks.filter((t) => t.kind !== "placeholder"); 20 - 21 - /** @type {ProxiedActions<InputActions>} */ 22 - const input = workerProxy(() => ports.input); 23 - 24 - /** @type {ProxiedActions<QueueEngineActions>} */ 25 - const queue = workerProxy(() => ports.queue); 26 - 27 - ports.input.start(); 28 - ports.queue.start(); 29 - 30 - // Consult input 31 - const groups = await input.groupConsult(cachedTracks); 32 - 33 - /** @type {Track[]} */ 34 - let availableTracks = []; 35 - 36 - Object.values(groups).forEach((value) => { 37 - if (value.available === false) return; 38 - availableTracks = availableTracks.concat(value.tracks); 39 - }, []); 40 - 41 - // Set pool 42 - await queue.supply({ tracks: availableTracks }); 43 - } 44 - 45 - //////////////////////////////////////////// 46 - // ⚡️ 47 - //////////////////////////////////////////// 48 - 49 - ostiary((context) => { 50 - rpc(context, { poolAvailable }); 51 - });
+142
src/components/orchestrator/scoped-tracks/element.js
··· 1 + import { 2 + BroadcastableDiffuseElement, 3 + query, 4 + queryOptional, 5 + } from "@common/element.js"; 6 + import { signal, untracked } from "@common/signal.js"; 7 + 8 + /** 9 + * @import {Track} from "@definitions/types.d.ts" 10 + * @import {ProxiedActions} from "@common/worker.d.ts" 11 + * @import {InputElement} from "@components/input/types.d.ts" 12 + * @import {OutputElement} from "@components/output/types.d.ts" 13 + * 14 + * @import {Actions} from "./types.d.ts" 15 + */ 16 + 17 + //////////////////////////////////////////// 18 + // ELEMENT 19 + //////////////////////////////////////////// 20 + 21 + class ScopedTracksOrchestrator extends BroadcastableDiffuseElement { 22 + static NAME = "diffuse/orchestrator/scoped-tracks"; 23 + static WORKER_URL = "components/orchestrator/scoped-tracks/worker.js"; 24 + 25 + /** @type {ProxiedActions<Actions>} */ 26 + #proxy; 27 + 28 + constructor() { 29 + super(); 30 + this.#proxy = this.workerProxy({ 31 + forceNew: { 32 + dependencies: { 33 + input: true, 34 + }, 35 + }, 36 + }); 37 + } 38 + 39 + // SIGNALS 40 + 41 + #input = signal(/** @type {InputElement | null} */ (null)); 42 + #output = signal(/** @type {OutputElement | null} */ (null)); 43 + 44 + #search = signal( 45 + /** @type {import("@components/processor/search/element.js").CLASS | null} */ (null), 46 + ); 47 + 48 + #scope = signal( 49 + /** @type {import("@components/engine/scope/element.js").CLASS | null} */ (null), 50 + ); 51 + 52 + #tracks = signal(/** @type {Track[]} */ ([])); 53 + 54 + // STATE 55 + 56 + tracks = this.#tracks.get; 57 + 58 + // LIFECYCLE 59 + 60 + /** 61 + * @override 62 + */ 63 + async connectedCallback() { 64 + // Broadcast if needed 65 + if (this.hasAttribute("group")) { 66 + this.broadcast(this.nameWithGroup, {}); 67 + } 68 + 69 + // Super 70 + super.connectedCallback(); 71 + 72 + /** @type {InputElement} */ 73 + const input = query(this, "input-selector"); 74 + 75 + /** @type {OutputElement} */ 76 + const output = query(this, "output-selector"); 77 + 78 + /** @type {import("@components/processor/search/element.js").CLASS} */ 79 + const search = query(this, "search-processor-selector"); 80 + 81 + /** @type {import("@components/engine/scope/element.js").CLASS | null} */ 82 + const scope = queryOptional(this, "scope-engine-selector"); 83 + 84 + // Assign to self 85 + this.#input.value = input; 86 + this.#output.value = output; 87 + this.#search.value = search; 88 + if (scope) this.#scope.value = scope; 89 + 90 + // When defined 91 + await customElements.whenDefined(output.localName); 92 + if (scope) await customElements.whenDefined(scope.localName); 93 + 94 + // Watch tracks collection 95 + this.effect(async () => { 96 + const collection = output.tracks.collection(); 97 + if ((await this.isLeader()) === false) return; 98 + this.#proxy.supplyAvailable(collection); 99 + }); 100 + 101 + // Watch search supply 102 + this.effect(async () => { 103 + const _trigger = search.supplyFingerprint(); 104 + const searchTerm = this.#scope.value?.searchTerm(); 105 + 106 + if ((await this.isLeader()) === false) return; 107 + 108 + const searchResults = searchTerm 109 + ? await this.#search.value?.search({ term: searchTerm }) 110 + : untracked(() => output.tracks.collection()); 111 + 112 + // TODO: Playlist selection 113 + this.#tracks.value = searchResults ?? []; 114 + }); 115 + } 116 + 117 + // WORKERS 118 + 119 + /** 120 + * @override 121 + */ 122 + dependencies() { 123 + if (!this.#input.value) throw new Error("Input element not defined yet"); 124 + if (!this.#search.value) throw new Error("Search element not defined yet"); 125 + 126 + return { 127 + input: this.#input.value, 128 + search: this.#search.value, 129 + }; 130 + } 131 + } 132 + 133 + export default ScopedTracksOrchestrator; 134 + 135 + //////////////////////////////////////////// 136 + // REGISTER 137 + //////////////////////////////////////////// 138 + 139 + export const CLASS = ScopedTracksOrchestrator; 140 + export const NAME = "do-scoped-tracks"; 141 + 142 + customElements.define(NAME, CLASS);
-103
src/components/orchestrator/search-tracks/element.js
··· 1 - import { BroadcastableDiffuseElement, query } from "@common/element.js"; 2 - 3 - /** 4 - * @import {Track} from "@definitions/types.d.ts" 5 - * @import {ProxiedActions} from "@common/worker.d.ts" 6 - * @import {InputElement} from "@components/input/types.d.ts" 7 - * @import {OutputElement} from "@components/output/types.d.ts" 8 - * 9 - * @import {Actions} from "./types.d.ts" 10 - */ 11 - 12 - //////////////////////////////////////////// 13 - // ELEMENT 14 - //////////////////////////////////////////// 15 - 16 - /** 17 - * Fill the search supply automatically with 18 - * tracks whenever they have been loaded, 19 - * or the tracks collection changes. 20 - */ 21 - class SearchTracksOrchestrator extends BroadcastableDiffuseElement { 22 - static NAME = "diffuse/orchestrator/search-tracks"; 23 - static WORKER_URL = "components/orchestrator/search-tracks/worker.js"; 24 - 25 - /** @type {ProxiedActions<Actions>} */ 26 - #proxy; 27 - 28 - constructor() { 29 - super(); 30 - this.#proxy = this.workerProxy({ 31 - forceNew: { 32 - dependencies: { 33 - input: true, 34 - }, 35 - }, 36 - }); 37 - } 38 - 39 - // LIFECYCLE 40 - 41 - /** 42 - * @override 43 - */ 44 - async connectedCallback() { 45 - // Broadcast if needed 46 - if (this.hasAttribute("group")) { 47 - this.broadcast(this.nameWithGroup, {}); 48 - } 49 - 50 - // Super 51 - super.connectedCallback(); 52 - 53 - /** @type {InputElement} */ 54 - const input = query(this, "input-selector"); 55 - 56 - /** @type {OutputElement} */ 57 - const output = query(this, "output-selector"); 58 - 59 - /** @type {import("@components/processor/search/element.js").CLASS} */ 60 - const search = query(this, "search-processor-selector"); 61 - 62 - // Assign to self 63 - this.input = input; 64 - this.output = output; 65 - this.search = search; 66 - 67 - // When defined 68 - await customElements.whenDefined(this.output.localName); 69 - 70 - // Watch tracks collection 71 - this.effect(async () => { 72 - const collection = output.tracks.collection(); 73 - if ((await this.isLeader()) === false) return; 74 - this.#proxy.supplyAvailable(collection); 75 - }); 76 - } 77 - 78 - // WORKERS 79 - 80 - /** 81 - * @override 82 - */ 83 - dependencies() { 84 - if (!this.input) throw new Error("Input element not defined yet"); 85 - if (!this.search) throw new Error("Search element not defined yet"); 86 - 87 - return { 88 - input: this.input, 89 - search: this.search, 90 - }; 91 - } 92 - } 93 - 94 - export default SearchTracksOrchestrator; 95 - 96 - //////////////////////////////////////////// 97 - // REGISTER 98 - //////////////////////////////////////////// 99 - 100 - export const CLASS = SearchTracksOrchestrator; 101 - export const NAME = "do-search-tracks"; 102 - 103 - customElements.define(NAME, SearchTracksOrchestrator);
src/components/orchestrator/search-tracks/types.d.ts src/components/orchestrator/scoped-tracks/types.d.ts
src/components/orchestrator/search-tracks/worker.js src/components/orchestrator/scoped-tracks/worker.js
-1
src/components/transformer/output/refiner/default/element.js
··· 3 3 4 4 /** 5 5 * @import { OutputManagerDeputy } from "../../../../output/types.d.ts" 6 - * @import { Track } from "@definitions/types.d.ts" 7 6 */ 8 7 9 8 /**
+8 -4
src/index.vto
··· 75 75 - url: "components/engine/repeat-shuffle/element.js" 76 76 title: "Repeat & Shuffle" 77 77 desc: "Signals synced with local storage (classified by group) that decide if audio should be repeated and if the queue should be shuffled when filling it." 78 + - url: "components/engine/scope/element.js" 79 + title: "Scope" 80 + desc: > 81 + Signals that could influence the scope of a set of tracks. 78 82 79 83 input: 80 84 - url: "components/input/opensubsonic/element.js" ··· 105 109 orchestrators: 106 110 - url: "components/orchestrator/auto-queue/element.js" 107 111 title: "Automatic queue" 108 - desc: "Sets the given queue element pool whenever the tracks signal from the given output changes. Additionally it always fills the queue automatically based (shuffled or regular, based on repeat-shuffle engine)." 112 + desc: > Fill the queue automatically with non-manual items (shuffled or regular, based on repeat-shuffle engine). 109 113 - url: "components/orchestrator/input/element.js" 110 114 title: "Input" 111 115 desc: "**A default input configuration.** Contains all the inputs provided here." ··· 121 125 - url: "components/orchestrator/sources/element.js" 122 126 title: "Sources" 123 127 desc: "Monitor tracks from the given output to form a list of sources based on the input's sources return value." 124 - - url: "components/orchestrator/search-tracks/element.js" 125 - title: "Search ⭤ Tracks" 126 - desc: "Supplies tracks to the given search processor whenever the tracks collection changes." 128 + - url: "components/orchestrator/scoped-tracks/element.js" 129 + title: "Scoped Tracks" 130 + desc: "Supplies the tracks from the given output to the given search processor whenever the tracks collection changes. Additionally it can perform a search and other ways to reduce the scope of tracks based on the given scope engine. Provides a `tracks` signal similar to `output.tracks.collection`" 127 131 128 132 output: 129 133 - title: "Bytes / Automerge Repo"
+2 -1
src/themes/loader/constituent/index.vto
··· 125 125 {{ echo -}}foundation.engine.audio(){{- /echo }} 126 126 {{ echo -}}foundation.engine.queue(){{- /echo }} 127 127 {{ echo -}}foundation.engine.repeatShuffle(){{- /echo }} 128 + {{ echo -}}foundation.engine.scope(){{- /echo }} 128 129 129 130 {{ echo -}}foundation.orchestrator.autoQueue(){{- /echo }} 130 131 {{ echo -}}foundation.orchestrator.input(){{- /echo }} 131 132 {{ echo -}}foundation.orchestrator.output(){{- /echo }} 132 133 {{ echo -}}foundation.orchestrator.queueAudio(){{- /echo }} 133 134 {{ echo -}}foundation.orchestrator.processTracks(){{- /echo }} 134 - {{ echo -}}foundation.orchestrator.searchTracks(){{- /echo }} 135 + {{ echo -}}foundation.orchestrator.scopedTracks(){{- /echo }} 135 136 {{ echo -}}foundation.orchestrator.sources(){{- /echo }} 136 137 137 138 {{ echo -}}foundation.processor.artwork(){{- /echo }}
+10 -3
src/themes/webamp/index.js
··· 1 1 import "@components/input/opensubsonic/element.js"; 2 2 import "@components/input/s3/element.js"; 3 - import "@components/orchestrator/auto-queue/element.js"; 4 3 import "@components/orchestrator/input/element.js"; 5 4 import "@components/orchestrator/output/element.js"; 6 5 import "@components/orchestrator/process-tracks/element.js"; 7 - import "@components/orchestrator/search-tracks/element.js"; 8 6 import "@components/orchestrator/sources/element.js"; 9 7 import "@components/processor/metadata/element.js"; 10 8 11 9 import * as Input from "@components/configurator/input/element.js"; 12 10 import * as Queue from "@components/engine/queue/element.js"; 13 11 import * as Search from "@components/processor/search/element.js"; 12 + import * as ScopedTracks from "@components/orchestrator/scoped-tracks/element.js"; 14 13 15 14 import { component } from "@common/element.js"; 16 - import { effect, signal, untracked } from "@common/signal.js"; 15 + import { effect, untracked } from "@common/signal.js"; 17 16 18 17 import "./browser/element.js"; 19 18 import "./configurators/input/element.js"; ··· 31 30 const input = component(Input); 32 31 const queue = component(Queue); 33 32 const search = component(Search); 33 + const scopedTracks = component(ScopedTracks); 34 34 35 35 /** @type {OutputElement | null} */ 36 36 const output = document.querySelector("#output"); ··· 124 124 initiatedPlaylist = true; 125 125 amp.store.dispatch({ type: "BUFFER_TRACK", id: 0 }); 126 126 } 127 + }); 128 + 129 + /** 130 + * Fill queue supply with available tracks. 131 + */ 132 + effect(() => { 133 + queue.supply({ tracks: scopedTracks.tracks() }); 127 134 }); 128 135 129 136 /**
+2 -8
src/themes/webamp/index.vto
··· 171 171 process-when-ready 172 172 ></do-process-tracks> 173 173 174 - <do-queue-tracks 175 - input-selector="#input" 176 - output-selector="#output" 177 - queue-engine-selector="de-queue" 178 - ></do-queue-tracks> 179 - 180 - <do-search-tracks 174 + <do-scoped-tracks 181 175 input-selector="#input" 182 176 output-selector="#output" 183 177 search-processor-selector="dp-search" 184 - ></do-search-tracks> 178 + ></do-scoped-tracks> 185 179 186 180 <do-sources 187 181 input-selector="#input"