A music player that connects to your cloud/distributed storage.
at v4 144 lines 3.6 kB view raw
1import { BroadcastableDiffuseElement, query } from "@common/element.js"; 2import { signal, untracked } from "@common/signal.js"; 3 4/** 5 * @import {Track} from "@definitions/types.d.ts" 6 * @import {ProxiedActions} from "@common/worker.d.ts" 7 * @import {InputElement} from "@components/input/types.d.ts" 8 * @import {OutputElement} from "@components/output/types.d.ts" 9 * 10 * @import {Actions} from "./types.d.ts" 11 */ 12 13//////////////////////////////////////////// 14// ELEMENT 15//////////////////////////////////////////// 16 17/** 18 * Processes inputs into tracks whenever 19 * the already existing tracks are loaded 20 * from the assigned output element. 21 */ 22class ProcessTracksOrchestrator extends BroadcastableDiffuseElement { 23 static NAME = "diffuse/orchestrator/process-tracks"; 24 static WORKER_URL = "components/orchestrator/process-tracks/worker.js"; 25 26 /** @type {ProxiedActions<Actions>} */ 27 #proxy; 28 29 constructor() { 30 super(); 31 this.#proxy = this.workerProxy({ 32 forceNew: { 33 dependencies: { input: true }, 34 }, 35 }); 36 } 37 38 // SIGNALS 39 40 #isProcessing = signal(false); 41 42 // STATE 43 44 isProcessing = this.#isProcessing.get; 45 46 // LIFECYCLE 47 48 /** 49 * @override 50 */ 51 async connectedCallback() { 52 // Broadcast if needed 53 if (this.hasAttribute("group")) { 54 const actions = this.broadcast(this.nameWithGroup, { 55 process: { strategy: "leaderOnly", fn: this.process }, 56 }); 57 58 if (actions) this.process = actions.process; 59 } 60 61 // Super 62 super.connectedCallback(); 63 64 /** @type {InputElement} */ 65 const input = query(this, "input-selector"); 66 67 /** @type {OutputElement<Track[]>} */ 68 const output = query(this, "output-selector"); 69 70 /** @type {import("@components/processor/metadata/element.js").CLASS} */ 71 const metadataProcessor = query(this, "metadata-processor-selector"); 72 73 // Assign to self 74 this.input = input; 75 this.output = output; 76 this.metadataProcessor = metadataProcessor; 77 78 // Wait until defined 79 await customElements.whenDefined(output.localName); 80 81 // Process whenever tracks are initially loaded 82 if (this.hasAttribute("process-when-ready")) { 83 this.effect(() => { 84 const state = output.tracks.state(); 85 if (state !== "loaded") return; 86 87 const skip = /** @type {any} */ (import.meta).env 88 ?.DISABLE_AUTOMATIC_TRACKS_PROCESSING ?? false; 89 if (skip) return; 90 91 untracked(() => this.process()); 92 }); 93 } 94 } 95 96 // WORKERS 97 98 /** 99 * @override 100 */ 101 dependencies() { 102 if (!this.input) throw new Error("Input element not defined yet"); 103 if (!this.metadataProcessor) { 104 throw new Error("Metadata processor element not defined yet"); 105 } 106 107 return { 108 input: this.input, 109 metadataProcessor: this.metadataProcessor, 110 }; 111 } 112 113 // ACTIONS 114 115 async process() { 116 if (!this.output) return; 117 if (this.#isProcessing.value) return; 118 119 // Start 120 this.#isProcessing.value = true; 121 console.log("🪵 Processing initiated"); 122 123 const cachedTracks = this.output.tracks.collection(); 124 const result = await this.#proxy.process(cachedTracks); 125 126 // Save if collection changed 127 if (result) await this.output.tracks.save(result); 128 129 // Fin 130 console.log("🪵 Processing completed"); 131 this.#isProcessing.value = false; 132 } 133} 134 135export default ProcessTracksOrchestrator; 136 137//////////////////////////////////////////// 138// REGISTER 139//////////////////////////////////////////// 140 141export const CLASS = ProcessTracksOrchestrator; 142export const NAME = "do-process-tracks"; 143 144customElements.define(NAME, ProcessTracksOrchestrator);