alpha
Login
or
Join now
tokono.ma
/
diffuse-applets
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Experiment to rebuild Diffuse using web applets.
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Overview
Issues
Pulls
Pipelines
feat: queue-audio grouping
author
Steven Vandevelde
date
1 year ago
(Jun 12, 2025, 6:04 PM +0200)
commit
f58f2289
f58f2289e21c77d524085ea6460b4ad6ed926096
parent
a737e77f
a737e77ff527e8e5cd61751b2c65b991f7cb810d
+35
-133
9 changed files
Expand all
Collapse all
Unified
Split
src
pages
constituents
blur
artwork-controller
_applet.astro
index.astro
orchestrator
queue-audio-tracks
_applet.astro
_manifest.json
index.astro
single-queue
_manifest.json
scripts
themes
blur
index.ts
pilot
index.ts
styles
variables.css
+9
-82
src/pages/constituents/blur/artwork-controller/_applet.astro
Reviewed
···
77
77
78
78
.controller menu {
79
79
display: flex;
80
80
-
font-size: 70%;
80
80
+
font-size: var(--fs-2xs);
81
81
gap: var(--space-sm);
82
82
margin: var(--space-md) 0;
83
83
padding: 0;
···
203
203
<script>
204
204
import { FastAverageColor } from "fast-average-color";
205
205
206
206
-
import { computed, effect, type Signal, signal } from "spellcaster";
207
207
-
import { repeat, tags, text, type ElementConfigurator } from "spellcaster/hyperscript.js";
206
206
+
import { computed, effect, type Signal, signal, throttled } from "spellcaster";
207
207
+
import { tags, text, type ElementConfigurator } from "spellcaster/hyperscript.js";
208
208
209
209
import type { ManagedOutput, Track } from "@applets/core/types";
210
210
import {
···
215
215
reactive,
216
216
register,
217
217
trackArtworkCacheId,
218
218
-
wait,
219
218
} from "@scripts/applets/common";
220
220
-
import { arrayShuffle } from "@scripts/common";
221
219
import scope from "astro:scope";
222
220
223
221
////////////////////////////////////////////
···
249
247
queue: await applet<QueueEngine.State>("../../engine/queue", { groupId: groupId() }),
250
248
};
251
249
252
252
-
const orchestrator = {
250
250
+
const _orchestrator = {
253
251
inputCache: await applet("../../../orchestrator/input-cache"),
252
252
+
queueAudioTracks: await applet("../../../orchestrator/queue-audio-tracks", {
253
253
+
groupId: groupId(),
254
254
+
}),
254
255
};
255
256
256
257
const processor = {
···
264
265
reactive(
265
266
engine.audio,
266
267
(data) => data.items[engine.queue.data.now?.id ?? Infinity]?.isPlaying ?? false,
267
267
-
(isPlaying) => setIsPlaying(isPlaying),
268
268
+
(isPlaying) => throttled(() => setIsPlaying(isPlaying))(),
268
269
);
269
270
270
271
reactive(
271
272
engine.audio,
272
273
(data) => data.items[engine.queue.data.now?.id ?? Infinity]?.progress ?? 0,
273
273
-
(progress: number) => setProgress(progress),
274
274
+
setProgress,
274
275
);
275
276
276
277
////////////////////////////////////////////
277
278
// 🎢 QUEUE
278
279
////////////////////////////////////////////
279
280
280
280
-
// TODO: Shuffle, limit amount, etc.
281
281
-
async function fillQueue() {
282
282
-
await engine.queue.sendAction("add", arrayShuffle(configurator.output.data.tracks.collection), {
283
283
-
timeoutDuration: 60000,
284
284
-
});
285
285
-
}
286
286
-
287
287
-
// When the active audio has ended,
288
288
-
// shift the queue.
289
289
-
290
290
-
// NOTE:
291
291
-
// This could probably be optimised, but it works.
292
292
-
293
293
-
reactive(
294
294
-
engine.audio,
295
295
-
(data) => data.items[engine.queue.data.now?.id ?? Infinity]?.hasEnded ?? false,
296
296
-
(hasEnded) => {
297
297
-
if (hasEnded) engine.queue.sendAction("shift");
298
298
-
},
299
299
-
);
300
300
-
301
301
-
// When the active queue item has changed,
302
302
-
// coordinate the audio engine accordingly.
303
303
-
304
304
-
reactive(
305
305
-
engine.queue,
306
306
-
(data) => data.now?.id,
307
307
-
async () => {
308
308
-
const playingNow = engine.queue.data.now;
309
309
-
const volume = engine.audio.data.volume;
310
310
-
311
311
-
// Play new active queue item
312
312
-
// TODO: Take URL expiration timestamp into account
313
313
-
// TODO: Preload next queue item
314
314
-
engine.audio.sendAction(
315
315
-
"render",
316
316
-
{
317
317
-
audio: playingNow
318
318
-
? [
319
319
-
{
320
320
-
id: playingNow.id,
321
321
-
isPreload: false,
322
322
-
url: await inputUrl(configurator.input, playingNow.uri).then((a) => a?.url),
323
323
-
},
324
324
-
]
325
325
-
: // NOTE: This probably isn't correct, keep preloads?
326
326
-
[],
327
327
-
328
328
-
// TODO: Only play if currently playing, otherwise keep paused.
329
329
-
play: playingNow
330
330
-
? {
331
331
-
audioId: playingNow.id,
332
332
-
volume,
333
333
-
}
334
334
-
: undefined,
335
335
-
},
336
336
-
{
337
337
-
timeoutDuration: 60000,
338
338
-
},
339
339
-
);
340
340
-
341
341
-
// Add more tracks to the queue if needed
342
342
-
if (playingNow) fillQueue();
343
343
-
},
344
344
-
);
345
345
-
346
346
-
// Add tracks to the queue once the tracks have been loaded.
347
347
-
348
348
-
wait(configurator.output, (d) => d?.tracks.state === "loaded").then(() => {
349
349
-
reactive(configurator.output, (d) => d.tracks.cacheId, fillQueue);
350
350
-
});
351
351
-
352
281
// React to active queue item.
353
282
354
283
reactive(
···
462
391
463
392
function playPause() {
464
393
const audioId = engine.queue.data.now?.id;
465
465
-
466
466
-
console.log(isPlaying(), audioId);
467
394
468
395
if (isPlaying() && audioId) {
469
396
engine.audio.sendAction("pause", { audioId });
+1
-1
src/pages/index.astro
Reviewed
···
47
47
48
48
const orchestrators = [
49
49
{ url: "orchestrator/input-cache/", title: "Input caching" },
50
50
-
{ url: "orchestrator/single-queue/", title: "Single queue" },
50
50
+
{ url: "orchestrator/queue-audio-tracks/", title: "Queue ⭤ Audio ⭤ Tracks" },
51
51
];
52
52
53
53
const output = [
+6
src/pages/orchestrator/queue-audio-tracks/_manifest.json
Reviewed
···
1
1
+
{
2
2
+
"name": "diffuse/orchestrator/queue-audio-tracks",
3
3
+
"title": "Diffuse Orchestrator | Queue Audio Tracks",
4
4
+
"entrypoint": "index.html",
5
5
+
"actions": {}
6
6
+
}
+10
-14
src/pages/orchestrator/single-queue/_applet.astro
src/pages/orchestrator/queue-audio-tracks/_applet.astro
Reviewed
···
1
1
<script>
2
2
-
import type { ManagedOutput, ResolvedUri, Track } from "@applets/core/types.d.ts";
2
2
+
import type { ManagedOutput } from "@applets/core/types.d.ts";
3
3
import { applet, inputUrl, reactive, register, wait } from "@scripts/applets/common";
4
4
5
5
////////////////////////////////////////////
···
26
26
////////////////////////////////////////////
27
27
// ACTIONS
28
28
////////////////////////////////////////////
29
29
-
context.setActionHandler("fill", fill);
30
29
31
30
// TODO: Shuffle, limit track amount, etc.
32
32
-
async function fill(tracks: Track[]) {
31
31
+
async function fill() {
32
32
+
const tracks = configurator.output.data.tracks.collection;
33
33
+
33
34
await engine.queue.sendAction("add", tracks, {
34
35
timeoutDuration: 60000,
35
36
});
···
94
95
{
95
96
id: playingNow.id,
96
97
isPreload: false,
97
97
-
url: await inputUrl(configurator.input, playingNow.uri),
98
98
+
url: await inputUrl(configurator.input, playingNow.uri).then((a) => a?.url),
98
99
},
99
100
]
100
101
: // NOTE: This probably isn't correct, keep preloads?
···
112
113
);
113
114
114
115
// Add more tracks to the queue if needed
115
115
-
if (playingNow) fill(configurator.output.data.tracks.collection);
116
116
+
if (playingNow) fill();
116
117
},
117
118
);
118
119
119
120
////////////////////////////////////////////
120
120
-
// 🎻 [Connections → Configurators]
121
121
+
// 🏕️ [Connections → Output]
121
122
// 📦 OUTPUT
122
123
////////////////////////////////////////////
123
124
124
124
-
// Add tracks to the queue once the tracks have been loaded.
125
125
+
// Add tracks to the queue once the tracks have been loaded;
126
126
+
// and every time the collection changes.
125
127
126
128
wait(configurator.output, (d) => d?.tracks.state === "loaded").then(() => {
127
127
-
connect(
128
128
-
configurator.output,
129
129
-
(data) => data.tracks.cacheId,
130
130
-
() => {
131
131
-
fill(configurator.output.data.tracks.collection);
132
132
-
},
133
133
-
);
129
129
+
connect(configurator.output, (data) => data.tracks.cacheId, fill);
134
130
});
135
131
</script>
-16
src/pages/orchestrator/single-queue/_manifest.json
Reviewed
···
1
1
-
{
2
2
-
"name": "diffuse/orchestrator/single-queue",
3
3
-
"title": "Diffuse Orchestrator | Single queue",
4
4
-
"entrypoint": "index.html",
5
5
-
"actions": {
6
6
-
"fill": {
7
7
-
"title": "Fill",
8
8
-
"description": "Fill up the queue.",
9
9
-
"params_schema": {
10
10
-
"type": "array",
11
11
-
"items": { "type": "object" },
12
12
-
"description": "Array of tracks to be used to fill up the queue."
13
13
-
}
14
14
-
}
15
15
-
}
16
16
-
}
src/pages/orchestrator/single-queue/index.astro
src/pages/orchestrator/queue-audio-tracks/index.astro
Reviewed
-14
src/scripts/themes/blur/index.ts
Reviewed
···
8
8
////////////////////////////////////////////
9
9
const ui = {};
10
10
11
11
-
////////////////////////////////////////////
12
12
-
// ⚙️ [Connections → Engines]
13
13
-
// 🔉 AUDIO
14
14
-
////////////////////////////////////////////
15
15
-
16
16
-
// TODO
17
17
-
18
18
-
////////////////////////////////////////////
19
19
-
// 🌅 [Connections → UI]
20
20
-
// 🔉 AUDIO
21
21
-
////////////////////////////////////////////
22
22
-
23
23
-
// TODO
24
24
-
25
11
// TESTING
26
12
27
13
// wait(configurator.output, (data) => data?.tracks?.state === "loaded").then(async () => {
+6
-6
src/scripts/themes/pilot/index.ts
Reviewed
···
20
20
21
21
const orchestrator = {
22
22
input: await applet("../../orchestrator/input-cache"),
23
23
-
queue: await applet("../../orchestrator/single-queue"),
23
23
+
queue: await applet("../../orchestrator/queue-audio-tracks"),
24
24
};
25
25
26
26
const ui = {
···
56
56
ui.audio,
57
57
(data) => data.isPlaying,
58
58
async (isPlaying, setter) => {
59
59
-
const trackId = engine.queue.data.now?.id;
59
59
+
const audioId = engine.queue.data.now?.id;
60
60
const volume = engine.audio.data.volume;
61
61
62
62
// Automatically start playing something if nothing is playing yet.
63
63
-
if (!trackId) {
63
63
+
if (!audioId) {
64
64
if (isPlaying) {
65
65
const now = await engine.queue.sendAction("shift");
66
66
if (!now) {
···
74
74
75
75
// Otherwise just control the audio
76
76
if (isPlaying) {
77
77
-
engine.audio.sendAction("play", { trackId, volume });
77
77
+
engine.audio.sendAction("play", { audioId, volume });
78
78
} else {
79
79
-
engine.audio.sendAction("pause", { trackId });
79
79
+
engine.audio.sendAction("pause", { audioId });
80
80
}
81
81
},
82
82
);
···
88
88
if (seekPosition !== undefined && engine.queue.data.now?.id) {
89
89
engine.audio.sendAction("seek", {
90
90
percentage: seekPosition,
91
91
-
trackId: engine.queue.data.now.id,
91
91
+
audioId: engine.queue.data.now.id,
92
92
});
93
93
}
94
94
},
+3
src/styles/variables.css
Reviewed
···
1
1
:root {
2
2
/* Font scales */
3
3
+
--fs-3xs: clamp(0.41rem, -0.06vi + 0.42rem, 0.38rem);
4
4
+
--fs-2xs: clamp(0.51rem, -0.02vi + 0.52rem, 0.5rem);
5
5
+
--fs-xs: clamp(0.64rem, 0.05vi + 0.63rem, 0.67rem);
3
6
--fs-sm: clamp(0.8rem, 0.17vi + 0.76rem, 0.89rem);
4
7
--fs-base: clamp(1rem, 0.34vi + 0.91rem, 1.19rem);
5
8
--fs-md: clamp(1.25rem, 0.61vi + 1.1rem, 1.58rem);