Experiment to rebuild Diffuse using web applets.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: Add & integrate storage/centre applet

+160 -19
+52
src/applets/storage/centre/applet.astro
··· 1 + <div id="container"></div> 2 + 3 + <script> 4 + import { applets } from "@web-applets/sdk"; 5 + import { State, Track } from "./types"; 6 + 7 + //////////////////////////////////////////// 8 + // SETUP 9 + //////////////////////////////////////////// 10 + const context = applets.register<State>(); 11 + 12 + const SAMPLE_SOURCE = { 13 + id: crypto.randomUUID(), 14 + meta: {}, 15 + 16 + appletURI: "TODO", 17 + }; 18 + 19 + const SAMPLE_TRACKS: Track[] = [ 20 + { 21 + id: crypto.randomUUID(), 22 + sourceId: SAMPLE_SOURCE.id, 23 + uri: "https://archive.org/download/SUSPENSE_Radio_Digitally_Restored_Collection/%2040-07-22%20The%20Lodger%20%28audition%29%20%28Herbert%20Marshall%2C%20Alfred%20Hitchcock%2C%20Edmund%20Gwenn%29.mp3", 24 + tags: { 25 + title: "Yours Truly, Johnny Dollar", 26 + }, 27 + }, 28 + { 29 + id: crypto.randomUUID(), 30 + sourceId: SAMPLE_SOURCE.id, 31 + uri: "https://archive.org/download/OTRR_Dimension_X_Singles/Dimension_X_1950-04-08__01_OuterLimit.mp3", 32 + tags: { 33 + title: "Dimension X", 34 + }, 35 + }, 36 + ]; 37 + 38 + // Initial state 39 + context.data = { 40 + sources: [SAMPLE_SOURCE], 41 + tracks: SAMPLE_TRACKS, // [] 42 + }; 43 + 44 + // State helpers 45 + function update(partial: Partial<State>): void { 46 + context.data = { ...context.data, ...partial }; 47 + } 48 + 49 + //////////////////////////////////////////// 50 + // ACTIONS 51 + //////////////////////////////////////////// 52 + </script>
+6
src/applets/storage/centre/manifest.json
··· 1 + { 2 + "name": "diffuse/storage/centre", 3 + "title": "Diffuse Storage | Centre", 4 + "entrypoint": "index.html", 5 + "actions": {} 6 + }
+39
src/applets/storage/centre/types.d.ts
··· 1 + export interface State<T = TrackTags> { 2 + sources: Source[]; 3 + tracks: Track<T>[]; 4 + } 5 + 6 + /* SOURCES */ 7 + 8 + export interface Source<Meta = Record<string, string>> { 9 + id: string; 10 + meta: Meta; 11 + 12 + // NOTE: This is associated with a data input applet. 13 + // For example, `diffuse.sh/storage/amazon-s3/` 14 + // 15 + // This association is needed to, for example, 16 + // optionally translate permanent URIs into usable 17 + // URLs in order actually play the audio track. 18 + appletURI: string; 19 + } 20 + 21 + /* TRACKS */ 22 + 23 + export interface Track<Tags = TrackTags> { 24 + id: string; 25 + sourceId: string; 26 + tags: Tags; 27 + 28 + // NOTE: This is a "semi-permanent" URI. 29 + // 30 + // Tracks are cached so you can't, for example, 31 + // use an URL that expires in several hours. 32 + uri: string; 33 + } 34 + 35 + export interface TrackTags { 36 + album?: string; 37 + artist?: string; 38 + title: string; 39 + }
+1
src/pages/themes/pilot/index.astro
··· 14 14 <!-- Other applets --> 15 15 <iframe id="applet__engine__audio" src="../../engine/audio/"></iframe> 16 16 <iframe id="applet__engine__queue" src="../../engine/queue/"></iframe> 17 + <iframe id="applet__storage__centre" src="../../storage/centre/"></iframe> 17 18 </Page>
+60 -18
src/scripts/themes/pilot/index.ts
··· 9 9 // 🗂️ Applets 10 10 //////////////////////////////////////////// 11 11 import type * as AudioEngine from "../../../applets/engine/audio/types.d.ts"; 12 + import type * as QueueEngine from "../../../applets/engine/queue/types.d.ts"; 13 + 14 + import type * as CentreStorage from "../../../applets/storage/centre/types.d.ts"; 15 + 12 16 import type * as AudioUI from "../../../applets/themes/pilot/ui/audio/types.ts"; 13 - import type * as QueueEngine from "../../../applets/engine/queue/types.d.ts"; 14 17 15 18 const engine = { 16 19 audio: await applet<AudioEngine.State>("../../engine/audio"), 17 - queue: await applet<AudioEngine.State>("../../engine/queue"), 20 + queue: await applet<QueueEngine.State>("../../engine/queue"), 21 + }; 22 + 23 + const storage = { 24 + centre: await applet<CentreStorage.State>("../../storage/centre"), 18 25 }; 19 26 20 27 const ui = { ··· 37 44 38 45 // NOTE: 39 46 // These could probably be optimised, but it works. 40 - // 41 47 42 48 reactive( 43 49 engine.audio, ··· 66 72 // ⚙️ [Connections → Engines] 67 73 // 🚏 QUEUE 68 74 //////////////////////////////////////////// 75 + 69 76 reactive( 70 77 engine.queue, 71 78 (data: QueueEngine.State) => data.now, ··· 93 100 ); 94 101 95 102 //////////////////////////////////////////// 103 + // 📦 [Connections → Storages] 104 + // ☀️ CENTRE 105 + //////////////////////////////////////////// 106 + 107 + // Track changes to in-memory user data, 108 + // reflect to local & remote data stores. 109 + // TODO: Make configurator applet. 110 + 111 + reactive( 112 + storage.centre, 113 + (data) => data, 114 + (data) => { 115 + // TODO: Store locally 116 + // TODO: Store remotely 117 + // 118 + // NOTE: 119 + // Make data object an automerge document 120 + // and compact on every page load? 121 + // We should be able to track what property 122 + // of the data has changed. 123 + }, 124 + ); 125 + 126 + reactive( 127 + storage.centre, 128 + (data: CentreStorage.State) => data.tracks, 129 + (tracks) => { 130 + // TODO: Need an action on the queue to fill it. 131 + // TODO: Need to translate the semi-permanent track uri 132 + // into a usable audio URL. 133 + engine.queue.sendAction( 134 + "add", 135 + tracks.map( 136 + (track) => { 137 + return { 138 + expiresAt: Infinity, 139 + id: track.id, 140 + url: track.uri, 141 + }; 142 + }, 143 + ), 144 + ); 145 + }, 146 + ); 147 + 148 + // NOTE: How do we sync new remote data with our local/in-memory data? 149 + 150 + //////////////////////////////////////////// 96 151 // 🌅 [Connections → UI] 97 152 // 🔉 AUDIO 98 153 //////////////////////////////////////////// 154 + 99 155 reactive( 100 156 ui.audio, 101 157 (data: AudioUI.State) => data.isPlaying, ··· 135 191 // 🚀 136 192 //////////////////////////////////////////// 137 193 138 - // TODO: Replace with an actual music collection 139 - await engine.queue.sendAction("add", [ 140 - { 141 - id: "Yours Truly, Johnny Dollar", 142 - expiresAt: Infinity, 143 - url: 144 - "https://archive.org/download/SUSPENSE_Radio_Digitally_Restored_Collection/%2040-07-22%20The%20Lodger%20%28audition%29%20%28Herbert%20Marshall%2C%20Alfred%20Hitchcock%2C%20Edmund%20Gwenn%29.mp3", 145 - }, 146 - { 147 - id: "Dimension X", 148 - expiresAt: Infinity, 149 - url: 150 - "https://archive.org/download/OTRR_Dimension_X_Singles/Dimension_X_1950-04-08__01_OuterLimit.mp3", 151 - }, 152 - ]); 194 + // ?
+2 -1
src/styles/themes/pilot/index.css
··· 50 50 /*********************************** 51 51 * Applets (No UI) 52 52 ***********************************/ 53 - iframe[src*="/engine/"] { 53 + iframe[src*="/engine/"], 54 + iframe[src*="/storage/"] { 54 55 height: 0; 55 56 left: 110vw; 56 57 opacity: 0;