Experiment to rebuild Diffuse using web applets.
1<script>
2 import type { ManagedOutput, ResolvedUri, Track } from "@applets/core/types.d.ts";
3 import { applet, register, wait } from "@scripts/applet/common";
4
5 ////////////////////////////////////////////
6 // SETUP
7 ////////////////////////////////////////////
8 const context = register<{ isProcessing: boolean }>();
9
10 // Initial data
11 context.data = {
12 isProcessing: false,
13 };
14
15 // Applet connections
16 const configurator = {
17 input: applet("/configurator/input"),
18 output: applet<ManagedOutput>("/configurator/output"),
19 };
20
21 const processor = {
22 metadata: applet("/processor/metadata"),
23 };
24
25 // Start processing once settled and tracks are loaded
26 context
27 .settled()
28 .then(() => configurator.output)
29 .then((output) => wait(output, (d) => d?.tracks.state === "loaded"))
30 .then(() => (context.isMainInstance() ? process() : undefined));
31
32 ////////////////////////////////////////////
33 // ACTIONS
34 ////////////////////////////////////////////
35 context.setActionHandler("process", process);
36
37 async function process() {
38 if (context.data.isProcessing) return;
39 context.data = { ...context.data, isProcessing: true };
40 console.log("🪵 Processing initiated");
41
42 const input = await configurator.input;
43 const output = await configurator.output;
44
45 const cachedTracks = output.data.tracks.collection;
46
47 await input.sendAction("contextualize", cachedTracks, {
48 timeoutDuration: 60000 * 5,
49 });
50
51 const tracks = await input.sendAction<Track[]>("list", cachedTracks, {
52 timeoutDuration: 60000 * 60 * 24,
53 });
54
55 // Process
56 let changed = true; // TODO
57
58 const tracksWithMetadata = await tracks.reduce(
59 async (promise: Promise<Track[]>, track: Track) => {
60 const acc = await promise;
61
62 if (track.tags && track.stats) return [...acc, track];
63
64 const resGet = await input.sendAction<ResolvedUri>(
65 "resolve",
66 { method: "GET", uri: track.uri },
67 {
68 timeoutDuration: 60000 * 5,
69 },
70 );
71
72 const resHead = await input.sendAction<ResolvedUri>(
73 "resolve",
74 { method: "HEAD", uri: track.uri },
75 {
76 timeoutDuration: 60000 * 5,
77 },
78 );
79
80 if (!resGet) return [...acc, track];
81
82 const metadataProcessor = await processor.metadata;
83 const { stats, tags } = await metadataProcessor.sendAction(
84 "supply",
85 { urls: { get: resGet.url, head: resHead?.url || resGet.url } },
86 {
87 timeoutDuration: 60000 * 15,
88 },
89 );
90
91 console.log(stats, tags);
92 changed = true;
93
94 return [...acc, { ...track, stats, tags }];
95 },
96 Promise.resolve([]),
97 );
98
99 // Save
100 if (changed)
101 await output.sendAction("tracks", tracksWithMetadata, {
102 timeoutDuration: 60000 * 5,
103 });
104
105 // Fin
106 console.log("🪵 Processing completed");
107 context.data = { ...context.data, isProcessing: false };
108 }
109</script>