A music player that connects to your cloud/distributed storage.
0
fork

Configure Feed

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

chore: improve docs

+364 -80
+317 -56
src/_data/elements.js
··· 4 4 /** @type {Record<string, string>} */ 5 5 const sources = {}; 6 6 7 - for await (const entry of walk(srcDir + "components", { match: [/element\.js$/] })) { 7 + for await ( 8 + const entry of walk(srcDir + "components", { match: [/element\.js$/] }) 9 + ) { 8 10 const content = await Deno.readTextFile(entry.path); 9 11 const key = entry.path.slice(srcDir.length); 10 12 sources[key] = content; ··· 14 16 sources, 15 17 16 18 artwork: [ 17 - { url: "components/artwork/audio-metadata/element.js", title: "Audio Metadata", desc: "Extracts embedded artwork from audio files using the music-metadata library." }, 18 - { url: "components/artwork/input/element.js", title: "Input", desc: "Fetches artwork by delegating to the configured input element's artwork method." }, 19 - { url: "components/artwork/last.fm/element.js", title: "Last.fm", desc: "Fetches cover art from the Last.fm API using track artist and album tags." }, 20 - { url: "components/artwork/musicbrainz/element.js", title: "MusicBrainz", desc: "Fetches cover art from MusicBrainz and the Cover Art Archive using track artist and album tags." }, 19 + { 20 + url: "components/artwork/audio-metadata/element.js", 21 + title: "Audio Metadata", 22 + desc: 23 + "Extracts embedded artwork from audio files using the music-metadata library.", 24 + }, 25 + { 26 + url: "components/artwork/input/element.js", 27 + title: "Input", 28 + desc: 29 + "Fetches artwork by delegating to the configured input element's artwork method.", 30 + }, 31 + { 32 + url: "components/artwork/last.fm/element.js", 33 + title: "Last.fm", 34 + desc: 35 + "Fetches cover art from the Last.fm API using track artist and album tags.", 36 + }, 37 + { 38 + url: "components/artwork/musicbrainz/element.js", 39 + title: "MusicBrainz", 40 + desc: 41 + "Fetches cover art from MusicBrainz and the Cover Art Archive using track artist and album tags.", 42 + }, 21 43 ], 22 44 23 45 configurators: [ 24 - { url: "components/configurator/artwork/element.js", title: "Artwork", desc: "Takes artwork components as children and tries each in sequence, returning the first non-null result." }, 25 - { url: "components/configurator/input/element.js", title: "Input", desc: "Allows for multiple inputs to be used at once." }, 26 - { url: "components/configurator/metadata/element.js", title: "Metadata", desc: "Takes metadata components as children and chains their patches in sequence." }, 27 - { url: "components/configurator/output/element.js", title: "Output", desc: "Enables the user to configure a specific output. If no default output is set, it creates a temporary session by storing everything in memory." }, 28 - { url: "components/configurator/scrobbles/element.js", title: "Scrobbles", desc: "Configure multiple scrobblers (music trackers)." }, 46 + { 47 + url: "components/configurator/artwork/element.js", 48 + title: "Artwork", 49 + desc: 50 + "Takes artwork components as children and tries each in sequence, returning the first non-null result.", 51 + }, 52 + { 53 + url: "components/configurator/input/element.js", 54 + title: "Input", 55 + desc: "Allows for multiple inputs to be used at once.", 56 + }, 57 + { 58 + url: "components/configurator/metadata/element.js", 59 + title: "Metadata", 60 + desc: 61 + "Takes metadata components as children and chains their patches in sequence.", 62 + }, 63 + { 64 + url: "components/configurator/output/element.js", 65 + title: "Output", 66 + desc: 67 + "Enables the user to configure a specific output. If no default output is set, it creates a temporary session by storing everything in memory.", 68 + }, 69 + { 70 + url: "components/configurator/scrobbles/element.js", 71 + title: "Scrobbles", 72 + desc: "Configure multiple scrobblers (music trackers).", 73 + }, 29 74 ], 30 75 31 76 engines: [ 32 - { url: "components/engine/audio/element.js", title: "Audio", desc: "Plays audio through audio elements." }, 33 - { url: "components/engine/queue/element.js", title: "Queue", desc: "A queue for tracks." }, 34 - { url: "components/engine/repeat-shuffle/element.js", title: "Repeat & Shuffle", desc: "Signals synced with local storage (classified by group) that decide if audio should be repeated and if the queue should be shuffled when filling it." }, 35 - { url: "components/engine/scope/element.js", title: "Scope", desc: "Signals that could influence the scope of a set of tracks." }, 77 + { 78 + url: "components/engine/audio/element.js", 79 + title: "Audio", 80 + desc: "Plays audio through audio elements.", 81 + }, 82 + { 83 + url: "components/engine/queue/element.js", 84 + title: "Queue", 85 + desc: "A queue for tracks.", 86 + }, 87 + { 88 + url: "components/engine/repeat-shuffle/element.js", 89 + title: "Repeat & Shuffle", 90 + desc: 91 + "Signals synced with local storage (classified by group) that decide if audio should be repeated and if the queue should be shuffled when filling it.", 92 + }, 93 + { 94 + url: "components/engine/scope/element.js", 95 + title: "Scope", 96 + desc: "Signals that could influence the scope of a set of tracks.", 97 + }, 36 98 ], 37 99 38 100 input: [ 39 - { url: "components/input/dropbox/element.js", title: "Dropbox", desc: "Audio files from Dropbox, using the Dropbox v2 HTTP API." }, 40 - { url: "components/input/ephemeral-cache/element.js", title: "Ephemeral Cache", desc: "Wraps another input and caches its track listing in memory for the duration of the session." }, 41 - { url: "components/input/https/element.js", title: "HTTPS", desc: "HTTPS URLs to audio files or streams." }, 42 - { url: "components/input/icecast/element.js", title: "Icecast", desc: "Icecast internet radio streams. Fetches ICY metadata to populate track information." }, 43 - { url: "components/input/local/element.js", title: "Local", desc: "Audio files or directories from your local device, using the browser's File System Access API." }, 44 - { url: "components/input/opensubsonic/element.js", title: "Opensubsonic", desc: "Add any (open)subsonic server." }, 45 - { url: "components/input/s3/element.js", title: "S3", desc: "AWS S3 and services that provide the same surface API such as Cloudflare R2." }, 46 - { url: "components/input/webdav/element.js", title: "WebDAV", desc: "Add any WebDAV server." }, 101 + { 102 + url: "components/input/dropbox/element.js", 103 + title: "Dropbox", 104 + desc: "Dropbox, using the Dropbox v2 HTTP API.", 105 + }, 106 + { 107 + url: "components/input/ephemeral-cache/element.js", 108 + title: "Ephemeral Cache", 109 + desc: 110 + "Ephemeral blobs stored in indexedDB, resolving creates temporary Blob URLs. Not responsible for storing blobs.", 111 + }, 112 + { 113 + url: "components/input/https/element.js", 114 + title: "HTTPS", 115 + desc: "HTTPS URLs to audio files or streams.", 116 + }, 117 + { 118 + url: "components/input/icecast/element.js", 119 + title: "Icecast", 120 + desc: 121 + "Icecast internet radio streams. Fetches ICY metadata to populate track information.", 122 + }, 123 + { 124 + url: "components/input/local/element.js", 125 + title: "Local", 126 + desc: 127 + "Audio files or directories from your local device, using the browser's File System Access API.", 128 + }, 129 + { 130 + url: "components/input/opensubsonic/element.js", 131 + title: "Opensubsonic", 132 + desc: "(Open)subsonic audio servers.", 133 + }, 134 + { 135 + url: "components/input/s3/element.js", 136 + title: "S3", 137 + desc: 138 + "AWS S3 and services that provide the same surface API such as Cloudflare R2.", 139 + }, 140 + { 141 + url: "components/input/webdav/element.js", 142 + title: "WebDAV", 143 + desc: 144 + "WebDAV servers. Depends on a service worker handling the `diffuse:basic-auth` query parameter and converting it to a `Authorization` header.", 145 + }, 47 146 ], 48 147 49 148 metadata: [ 50 - { url: "components/metadata/audio-file/element.js", title: "Audio File", desc: "Extracts tags and audio stats from audio files using the music-metadata library." }, 149 + { 150 + url: "components/metadata/audio-file/element.js", 151 + title: "Audio File", 152 + desc: 153 + "Extracts tags and audio stats from audio files using the music-metadata library.", 154 + }, 51 155 ], 52 156 53 157 orchestrators: [ 54 - { url: "components/orchestrator/artwork/element.js", title: "Artwork", desc: "Fetches cover art for a given set of tracks, stored locally in indexedDB. Uses the artwork configurator to try each configured source in sequence." }, 55 - { url: "components/orchestrator/auto-queue/element.js", title: "Automatic Queue", desc: "Fill the queue automatically with non-manual items (shuffled or regular, based on repeat-shuffle engine)." }, 56 - { url: "components/orchestrator/controller/element.js", title: "Controller", desc: "Provides commonly used computed signals derived from the audio engine, queue engine, and output. Exposes currentTrack(), isPlaying(), and references to the underlying engines." }, 57 - { url: "components/orchestrator/cover-groups/element.js", title: "Cover Groups", desc: "Groups tracks by cover art to form collections." }, 58 - { url: "components/orchestrator/favourites/element.js", title: "Favourites", desc: "Mark tracks as favourites. Automatically creates an unordered 'Favourites' playlist." }, 59 - { url: "components/orchestrator/media-session/element.js", title: "Media Session", desc: "Keeps the browser/os media session in sync with queue and audio state. Adds handlers for previous, next, seek to, etc." }, 60 - { url: "components/orchestrator/output/element.js", title: "Output", desc: "A default output configuration. Contains all the outputs provided here along with the relevant transformers." }, 61 - { url: "components/orchestrator/path-collections/element.js", title: "Path Collections", desc: "Wraps an output element to generate ephemeral playlists based on the first path segment of each track's URI. Ephemeral items are excluded from storage." }, 62 - { url: "components/orchestrator/process-tracks/element.js", title: "Process Inputs Into Tracks", desc: "Whenever the cached tracks are initially loaded through the passed output element it will list tracks by using the passed input element. Afterwards it loops over all tracks and checks if metadata needs to be fetched. If anything has changed, it'll pass the results to the output element." }, 63 - { url: "components/orchestrator/queue-audio/element.js", title: "Queue ⭤ Audio", desc: "Connects the given queue engine to the given audio engine." }, 64 - { url: "components/orchestrator/scoped-tracks/element.js", title: "Scoped Tracks", desc: "Watches the given output's tracks collection and runs them through a built-in search index. Can perform a search and other ways to reduce the scope of tracks based on the given scope engine. Provides a tracks signal similar to output.tracks.collection." }, 65 - { url: "components/orchestrator/scrobble-audio/element.js", title: "Scrobble ⭤ Audio", desc: "Connects the audio engine with a scrobbler element. Calls nowPlaying when a track starts playing and scrobble once the user has listened long enough." }, 66 - { url: "components/orchestrator/sources/element.js", title: "Sources", desc: "Monitor tracks from the given output to form a list of sources based on the input's sources return value." }, 158 + { 159 + url: "components/orchestrator/artwork/element.js", 160 + title: "Artwork", 161 + desc: 162 + "Fetches cover art for a given set of tracks, stored locally in indexedDB. Uses the artwork configurator to try each configured source in sequence.", 163 + }, 164 + { 165 + url: "components/orchestrator/auto-queue/element.js", 166 + title: "Automatic Queue", 167 + desc: 168 + "Fill the queue automatically with non-manual items (shuffled or regular, based on repeat-shuffle engine).", 169 + }, 170 + { 171 + url: "components/orchestrator/controller/element.js", 172 + title: "Controller", 173 + desc: 174 + "Provides commonly used computed signals derived from the audio engine, queue engine, and output. Exposes currentTrack(), isPlaying(), and references to the underlying engines.", 175 + }, 176 + { 177 + url: "components/orchestrator/cover-groups/element.js", 178 + title: "Cover Groups", 179 + desc: "Groups tracks by cover art to form collections.", 180 + }, 181 + { 182 + url: "components/orchestrator/favourites/element.js", 183 + title: "Favourites", 184 + desc: 185 + "Mark tracks as favourites. Automatically creates an unordered 'Favourites' playlist.", 186 + }, 187 + { 188 + url: "components/orchestrator/media-session/element.js", 189 + title: "Media Session", 190 + desc: 191 + "Keeps the browser/os media session in sync with queue and audio state. Adds handlers for previous, next, seek to, etc.", 192 + }, 193 + { 194 + url: "components/orchestrator/output/element.js", 195 + title: "Output", 196 + desc: 197 + "A default output configuration. Contains all the outputs provided here along with the relevant transformers.", 198 + }, 199 + { 200 + url: "components/orchestrator/path-collections/element.js", 201 + title: "Path Collections", 202 + desc: 203 + "Wraps an output element to generate ephemeral playlists based on the first path segment of each track's URI. Ephemeral items are excluded from storage.", 204 + }, 205 + { 206 + url: "components/orchestrator/process-tracks/element.js", 207 + title: "Process Inputs Into Tracks", 208 + desc: 209 + "Whenever the cached tracks are initially loaded through the passed output element it will list tracks by using the passed input element. Afterwards it loops over all tracks and checks if metadata needs to be fetched. If anything has changed, it'll pass the results to the output element.", 210 + }, 211 + { 212 + url: "components/orchestrator/queue-audio/element.js", 213 + title: "Queue ⭤ Audio", 214 + desc: "Connects the given queue engine to the given audio engine.", 215 + }, 216 + { 217 + url: "components/orchestrator/scoped-tracks/element.js", 218 + title: "Scoped Tracks", 219 + desc: 220 + "Watches the given output's tracks collection and runs them through a built-in search index. Can perform a search and other ways to reduce the scope of tracks based on the given scope engine. Provides a tracks signal similar to output.tracks.collection.", 221 + }, 222 + { 223 + url: "components/orchestrator/scrobble-audio/element.js", 224 + title: "Scrobble ⭤ Audio", 225 + desc: 226 + "Connects the audio engine with a scrobbler element. Calls nowPlaying when a track starts playing and scrobble once the user has listened long enough.", 227 + }, 228 + { 229 + url: "components/orchestrator/sources/element.js", 230 + title: "Sources", 231 + desc: 232 + "Monitor tracks from the given output to form a list of sources based on the input's sources return value.", 233 + }, 67 234 ], 68 235 69 236 output: [ 70 - { url: "components/output/polymorphic/indexed-db/element.js", title: "Polymorphic / IndexedDB", desc: "Stores output into the local indexedDB. Supports any type of data that indexedDB supports." }, 71 - { url: "components/output/bytes/s3/element.js", title: "Bytes / S3", desc: "Store output data on AWS S3 or compatible services such as Cloudflare R2." }, 72 - { url: "components/output/raw/atproto/element.js", title: "Raw / AT Protocol", desc: "Store your user data on the storage associated with your ATProtocol identity. Data is lexicon shaped by default so this element takes in that data directly without any transformations." }, 237 + { 238 + url: "components/output/polymorphic/indexed-db/element.js", 239 + title: "Polymorphic / IndexedDB", 240 + desc: 241 + "Stores output into the local indexedDB. Supports any type of data that indexedDB supports.", 242 + }, 243 + { 244 + url: "components/output/bytes/s3/element.js", 245 + title: "Bytes / S3", 246 + desc: 247 + "Store output data on AWS S3 or compatible services such as Cloudflare R2.", 248 + }, 249 + { 250 + url: "components/output/raw/atproto/element.js", 251 + title: "Raw / AT Protocol", 252 + desc: 253 + "Store your user data on the storage associated with your ATProtocol identity. Data is lexicon shaped by default so this element takes in that data directly without any transformations.", 254 + }, 73 255 ], 74 256 75 257 supplements: [ 76 - { url: "components/supplement/last.fm/element.js", title: "Last.fm Scrobbler", desc: "Scrobbles track plays to Last.fm." }, 77 - { url: "components/supplement/listenbrainz/element.js", title: "ListenBrainz Scrobbler", desc: "Scrobbles track plays to ListenBrainz.", todo: true }, 78 - { url: "components/supplement/rocksky/element.js", title: "Rocksky Scrobbler", desc: "Scrobbles track plays to Rocksky.", todo: true }, 258 + { 259 + url: "components/supplement/last.fm/element.js", 260 + title: "Last.fm Scrobbler", 261 + desc: "Scrobbles track plays to Last.fm.", 262 + }, 263 + { 264 + url: "components/supplement/listenbrainz/element.js", 265 + title: "ListenBrainz Scrobbler", 266 + desc: "Scrobbles track plays to ListenBrainz.", 267 + todo: true, 268 + }, 269 + { 270 + url: "components/supplement/rocksky/element.js", 271 + title: "Rocksky Scrobbler", 272 + desc: "Scrobbles track plays to Rocksky.", 273 + todo: true, 274 + }, 79 275 ], 80 276 81 277 transformers: [ 82 - { url: "components/transformer/output/bytes/automerge/element.js", title: "Output / Bytes / Automerge", desc: "Translate data to and from an Automerge CRDT.", todo: true }, 83 - { url: "components/transformer/output/bytes/dasl-sync/element.js", title: "Output / Bytes / DASL Sync", desc: "Syncs data between local and remote using CID-based diffing and performs union merges with tombstone tracking when both sides have diverged." }, 84 - { url: "components/transformer/output/bytes/json/element.js", title: "Output / Bytes / JSON", desc: "Raw data schema output to and from JSON Uint8Array." }, 85 - { url: "components/transformer/output/raw/atproto-sync/element.js", title: "Output / Raw / AT Protocol Sync", desc: "Wraps an AT Protocol output with a local IndexedDB cache. Uses the repo revision to skip unnecessary fetches and performs union merges with tombstone tracking when both local and remote have diverged." }, 86 - { url: "components/transformer/output/refiner/default/element.js", title: "Output / Refiner / Default", desc: "Removes output state that is not meant to be saved to storage, such as ephemeral tracks. Ideally part of every theme." }, 87 - { url: "components/transformer/output/refiner/initial-contents/element.js", title: "Output / Refiner / Initial Contents", desc: "Sets the initial contents for an output on first load." }, 88 - { url: "components/transformer/output/refiner/passkey-encryption/element.js", title: "Output / Refiner / Track URI Passkey", desc: "Encrypts track URIs using a passkey-derived PRF key. On read, decrypts encrypted:// URIs transparently; on write, re-encrypts all URIs before passing downstream." }, 89 - { url: "components/transformer/output/string/json/element.js", title: "Output / String / JSON", desc: "Raw data schema output to and from JSON UTF8 string." }, 278 + { 279 + url: "components/transformer/output/bytes/automerge/element.js", 280 + title: "Output / Bytes / Automerge", 281 + desc: "Translate data to and from an Automerge CRDT.", 282 + todo: true, 283 + }, 284 + { 285 + url: "components/transformer/output/bytes/dasl-sync/element.js", 286 + title: "Output / Bytes / DASL Sync", 287 + desc: 288 + "Syncs data between local and remote using CID-based diffing and performs union merges with tombstone tracking when both sides have diverged.", 289 + }, 290 + { 291 + url: "components/transformer/output/bytes/json/element.js", 292 + title: "Output / Bytes / JSON", 293 + desc: "Raw data schema output to and from JSON Uint8Array.", 294 + }, 295 + { 296 + url: "components/transformer/output/raw/atproto-sync/element.js", 297 + title: "Output / Raw / AT Protocol Sync", 298 + desc: 299 + "Wraps an AT Protocol output with a local IndexedDB cache. Uses the repo revision to skip unnecessary fetches and performs union merges with tombstone tracking when both local and remote have diverged.", 300 + }, 301 + { 302 + url: "components/transformer/output/refiner/default/element.js", 303 + title: "Output / Refiner / Default", 304 + desc: 305 + "Removes output state that is not meant to be saved to storage, such as ephemeral tracks. Ideally part of every theme.", 306 + }, 307 + { 308 + url: "components/transformer/output/refiner/initial-contents/element.js", 309 + title: "Output / Refiner / Initial Contents", 310 + desc: "Sets the initial contents for an output on first load.", 311 + }, 312 + { 313 + url: 314 + "components/transformer/output/refiner/passkey-encryption/element.js", 315 + title: "Output / Refiner / Track URI Passkey", 316 + desc: 317 + "Encrypts track URIs using a passkey-derived PRF key. On read, decrypts encrypted:// URIs transparently; on write, re-encrypts all URIs before passing downstream.", 318 + }, 319 + { 320 + url: "components/transformer/output/string/json/element.js", 321 + title: "Output / String / JSON", 322 + desc: "Raw data schema output to and from JSON UTF8 string.", 323 + }, 90 324 ], 91 325 92 326 definitions: [ 93 - { url: "definitions/output/collaboration.json", title: "Output / Collaboration", desc: "Represents a collaboration between multiple collaborators on a subject, such as a playlist." }, 94 - { url: "definitions/output/facet.json", title: "Output / Facet", desc: "Facet pointer or HTML snippet." }, 95 - { url: "definitions/output/playlistItem.json", title: "Output / Playlist Item", desc: "Represents a single item in a playlist. Tracks are matched based on the given criteria. A playlist is formed by grouping items by their playlist property." }, 96 - { title: "Output / Progress", desc: "Used to track progress of (long) audio playback.", todo: true }, 97 - { url: "definitions/output/track.json", title: "Output / Track", desc: "Represents audio that can be played, or a placeholder for a source of tracks. Contains a URI that will resolve to the audio." }, 98 - { url: "definitions/output/trackBundle.json", title: "Output / Track Bundle", desc: "A bundle of tracks." }, 327 + { 328 + url: "definitions/output/collaboration.json", 329 + title: "Output / Collaboration", 330 + desc: 331 + "Represents a collaboration between multiple collaborators on a subject, such as a playlist.", 332 + }, 333 + { 334 + url: "definitions/output/facet.json", 335 + title: "Output / Facet", 336 + desc: "Facet pointer or HTML snippet.", 337 + }, 338 + { 339 + url: "definitions/output/playlistItem.json", 340 + title: "Output / Playlist Item", 341 + desc: 342 + "Represents a single item in a playlist. Tracks are matched based on the given criteria. A playlist is formed by grouping items by their playlist property.", 343 + }, 344 + { 345 + title: "Output / Progress", 346 + desc: "Used to track progress of (long) audio playback.", 347 + todo: true, 348 + }, 349 + { 350 + url: "definitions/output/track.json", 351 + title: "Output / Track", 352 + desc: 353 + "Represents audio that can be played, or a placeholder for a source of tracks. Contains a URI that will resolve to the audio.", 354 + }, 355 + { 356 + url: "definitions/output/trackBundle.json", 357 + title: "Output / Track Bundle", 358 + desc: "A bundle of tracks.", 359 + }, 99 360 ], 100 361 };
+3 -3
src/_data/facets.json
··· 57 57 }, 58 58 { 59 59 "url": "facets/connect/atproto/index.html", 60 - "title": "Connect / AT Protocol", 60 + "title": "Connect / Atmosphere", 61 61 "category": "Data", 62 62 "desc": "Use your AT Protocol identity for user-data storage." 63 63 }, ··· 89 89 "url": "facets/connect/opensubsonic/index.html", 90 90 "title": "Connect / OpenSubsonic", 91 91 "category": "Data", 92 - "desc": "Connect to an OpenSubsonic server for audio input." 92 + "desc": "Add an OpenSubsonic server as an audio source." 93 93 }, 94 94 { 95 95 "url": "facets/connect/s3/index.html", ··· 101 101 "url": "facets/connect/webdav/index.html", 102 102 "title": "Connect / WebDAV", 103 103 "category": "Data", 104 - "desc": "Connect to a WebDAV server for audio input." 104 + "desc": "Add a WebDAV server as an audio source." 105 105 }, 106 106 { 107 107 "url": "facets/data/artwork-bundle/index.html",
+6 -7
src/components/configurator/input/worker.js
··· 2 2 import * as URI from "fast-uri"; 3 3 import * as Cid from "~/common/cid.js"; 4 4 5 + import { 6 + CACHE_KEY_PREFIX, 7 + SCHEME as CACHE_SCHEME, 8 + } from "~/components/input/ephemeral-cache/constants.js"; 9 + 5 10 import { groupTracksPerScheme, groupUrisPerScheme } from "~/common/utils.js"; 6 11 import { ostiary, rpc, workerProxy } from "~/common/worker.js"; 7 12 ··· 11 16 * @import {ActionsWithTunnel, ProxiedActions} from "~/common/worker.d.ts" 12 17 * @import {Actions} from "./types.d.ts" 13 18 */ 14 - 15 - //////////////////////////////////////////// 16 - // LOCAL CACHE 17 - //////////////////////////////////////////// 18 - 19 - const CACHE_KEY_PREFIX = "diffuse/components/configurator/input/cache/"; 20 19 21 20 //////////////////////////////////////////// 22 21 // INPUT ACTIONS ··· 209 208 const buffer = await blob.arrayBuffer(); 210 209 const bytes = new Uint8Array(buffer); 211 210 const cid = await Cid.create(0x55, bytes); 212 - const uri = `ephemeral+cache://${cid}`; 211 + const uri = `${CACHE_SCHEME}://${cid}`; 213 212 if (await IDB.get(CACHE_KEY_PREFIX + uri) === undefined) { 214 213 await IDB.set(CACHE_KEY_PREFIX + uri, blob); 215 214 }
+1 -1
src/components/input/ephemeral-cache/constants.js
··· 1 1 export const SCHEME = "ephemeral+cache"; 2 - export const CACHE_KEY_PREFIX = "diffuse/components/configurator/input/cache/"; 2 + export const CACHE_KEY_PREFIX = "diffuse/components/input/ephemeral-cache/cache/";
+35 -11
src/components/input/webdav/common.js
··· 16 16 17 17 /** 18 18 * Build an HTTP(S) URL with credentials in a query param for the service worker to intercept. 19 - * Credentials go in `?_auth=<base64>` rather than `user:pass@host` because browsers 19 + * Credentials go in `?diffuse:basic-auth=<base64>` rather than `user:pass@host` because browsers 20 20 * block `new Request()` with credentials in the URL authority (which music-metadata uses). 21 21 * 22 22 * @param {Server} server ··· 24 24 */ 25 25 export function buildTrackUrl(server, filePath = "") { 26 26 const url = new URL(toHttpUrl(server, filePath)); 27 - url.searchParams.set("diffuse:basic-auth", btoa(unescape(encodeURIComponent(`${server.username}:${server.password}`)))); 27 + url.searchParams.set( 28 + "diffuse:basic-auth", 29 + btoa(unescape(encodeURIComponent(`${server.username}:${server.password}`))), 30 + ); 28 31 return url.href; 29 32 } 30 33 ··· 49 52 50 53 return URI.serialize({ 51 54 scheme: SCHEME, 52 - userinfo: `${encodeURIComponent(server.username)}:${encodeURIComponent(server.password)}`, 55 + userinfo: `${encodeURIComponent(server.username)}:${ 56 + encodeURIComponent(server.password) 57 + }`, 53 58 host, 54 59 path: filePath, 55 60 query: QS.stringify({ dir: server.dir, protocol }), ··· 67 72 68 73 const userinfo = uri.userinfo ?? ""; 69 74 const colonIdx = userinfo.indexOf(":"); 70 - const username = decodeURIComponent(colonIdx >= 0 ? userinfo.slice(0, colonIdx) : userinfo); 71 - const password = decodeURIComponent(colonIdx >= 0 ? userinfo.slice(colonIdx + 1) : ""); 75 + const username = decodeURIComponent( 76 + colonIdx >= 0 ? userinfo.slice(0, colonIdx) : userinfo, 77 + ); 78 + const password = decodeURIComponent( 79 + colonIdx >= 0 ? userinfo.slice(colonIdx + 1) : "", 80 + ); 72 81 73 82 const qs = QS.parse(uri.query || ""); 74 83 const dir = typeof qs.dir === "string" ? qs.dir : "/"; ··· 89 98 export function toHttpUrl(server, path = "") { 90 99 const base = server.host.includes("://") 91 100 ? server.host 92 - : `${server.host.split(":")[0] === "localhost" || server.host.split(":")[0] === "127.0.0.1" ? "http" : "https"}://${server.host}`; 101 + : `${ 102 + server.host.split(":")[0] === "localhost" || 103 + server.host.split(":")[0] === "127.0.0.1" 104 + ? "http" 105 + : "https" 106 + }://${server.host}`; 93 107 94 108 return base.replace(/\/$/, "") + (path ? "/" + path.replace(/^\//, "") : ""); 95 109 } ··· 98 112 * @param {Server} server 99 113 */ 100 114 export function authHeader(server) { 101 - return `Basic ${btoa(unescape(encodeURIComponent(`${server.username}:${server.password}`)))}`; 115 + return `Basic ${ 116 + btoa(unescape(encodeURIComponent(`${server.username}:${server.password}`))) 117 + }`; 102 118 } 103 119 104 120 /** ··· 244 260 if (child.type !== "element") continue; 245 261 246 262 if (child.name.local === "href") { 247 - href = (child.children?.find((n) => n.type === "text")?.text ?? "").trim(); 263 + href = (child.children?.find((n) => n.type === "text")?.text ?? "") 264 + .trim(); 248 265 } else if (child.name.local === "propstat") { 249 266 if (propstatHasCollection(child)) isCollection = true; 250 267 } ··· 269 286 // Normalise both sides to have a leading slash — server hrefs always do, 270 287 // but `dir` may not when the user omitted the leading slash in the form. 271 288 const normPath = path.replace(/\/$/, ""); 272 - const normDir = ("/" + decodeURIComponent(dir).replace(/^\//, "")).replace(/\/$/, ""); 289 + const normDir = ("/" + decodeURIComponent(dir).replace(/^\//, "")).replace( 290 + /\/$/, 291 + "", 292 + ); 273 293 if (normPath === normDir) continue; 274 294 275 295 // Skip Synology extended-attribute metadata folders ··· 298 318 for (const prop of propstat.children ?? []) { 299 319 if (prop.type !== "element" || prop.name?.local !== "prop") continue; 300 320 for (const child of prop.children ?? []) { 301 - if (child.type !== "element" || child.name?.local !== "resourcetype") continue; 321 + if (child.type !== "element" || child.name?.local !== "resourcetype") { 322 + continue; 323 + } 302 324 for (const rt of child.children ?? []) { 303 - if (rt.type === "element" && rt.name?.local === "collection") return true; 325 + if (rt.type === "element" && rt.name?.local === "collection") { 326 + return true; 327 + } 304 328 } 305 329 } 306 330 }
+2 -2
src/facets/connect/atproto/index.inline.js
··· 7 7 8 8 import { setup } from "~/facets/connect/common.js"; 9 9 10 - foundation.setup({ title: "Connect AT Protocol | Diffuse" }); 10 + foundation.setup({ title: "Connect Atmosphere | Diffuse" }); 11 11 12 12 /** 13 13 * @import { ATProtoOutputElement } from "~/components/output/raw/atproto/types.d.ts" ··· 48 48 //////////////////////////////////////////// 49 49 50 50 const { setItems } = setup({ 51 - title: "AT Protocol", 51 + title: "Atmosphere", 52 52 hasInput: false, 53 53 54 54 description: html`