A music player that connects to your cloud/distributed storage.
1import { BroadcastableDiffuseElement, query } from "~/common/element.js";
2
3/**
4 * @import {DiffuseElement} from "~/common/element.js";
5 * @import {SignalReader} from "~/common/signal.d.ts";
6 * @import {Track} from "~/definitions/types.d.ts"
7 * @import RepeatShuffleEngine from "~/components/engine/repeat-shuffle/element.js"
8 */
9
10////////////////////////////////////////////
11// ELEMENT
12////////////////////////////////////////////
13
14/**
15 * Update the queue pool whenever tracks have been loaded,
16 * or the tracks collection changes.
17 */
18class AutoTracksOrchestrator extends BroadcastableDiffuseElement {
19 static NAME = "diffuse/orchestrator/auto-queue";
20
21 // LIFECYCLE
22
23 /**
24 * @override
25 */
26 async connectedCallback() {
27 // Broadcast if needed
28 if (this.hasAttribute("group")) {
29 this.broadcast(this.identifier, {});
30 }
31
32 // Super
33 super.connectedCallback();
34
35 /** @type {import("~/components/engine/queue/element.js").CLASS} */
36 const queue = query(this, "queue-engine-selector");
37
38 /** @type {RepeatShuffleEngine} */
39 const repeatShuffle = query(this, "repeat-shuffle-engine-selector");
40
41 /** @type {DiffuseElement & { tracks: SignalReader<Track[]> }} */
42 const tracksElement = query(this, "tracks-selector");
43
44 // When defined
45 await customElements.whenDefined(queue.localName);
46 await customElements.whenDefined(repeatShuffle.localName);
47 await customElements.whenDefined(tracksElement.localName);
48
49 // Watch tracks
50 this.effect(() => {
51 const tracks = tracksElement.tracks();
52
53 this.isLeader().then(async (isLeader) => {
54 if (!isLeader) return;
55 queue.supply({ trackIds: tracks.map((t) => t.id) });
56 });
57 });
58
59 // Automatically fill queue
60 let lastShuffle = repeatShuffle.shuffle();
61 let lastFingerprint = queue.supplyFingerprint();
62
63 this.effect(() => {
64 const trigger = queue.now();
65 const fingerprint = queue.supplyFingerprint();
66 const shuffled = repeatShuffle.shuffle();
67
68 this.isLeader().then((isLeader) => {
69 if (!isLeader) return;
70
71 // Clear non-manual items from the queue
72 // when 'shuffle' gets turned off or on;
73 // or when queue supply changes.
74 if (shuffled !== lastShuffle || fingerprint !== lastFingerprint) {
75 lastShuffle = shuffled;
76 lastFingerprint = fingerprint;
77 queue.clear({ manualOnly: true });
78 }
79
80 queue.fill({ amount: 10, shuffled: repeatShuffle.shuffle() });
81
82 // Insert now-playing track if there's none
83 if (!trigger) queue.shift();
84 });
85 });
86 }
87}
88
89export default AutoTracksOrchestrator;
90
91////////////////////////////////////////////
92// REGISTER
93////////////////////////////////////////////
94
95export const CLASS = AutoTracksOrchestrator;
96export const NAME = "do-auto-queue";
97
98customElements.define(NAME, CLASS);