A music player that connects to your cloud/distributed storage. diffuse.sh
Something went wrong. Try again.
4.7 kB · 155 lines
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156import * as TID from "@atcute/tid";import { html } from "lit-html";
import * as Output from "~/common/output.js";import { SCHEME } from "~/components/input/https-json/constants.js";import { buildURI, parseURI, serverId } from "~/components/input/https-json/common.js";import { effect } from "~/common/signal.js";import foundation from "~/common/foundation.js";
import { setup } from "~/facets/connect/common.js";
/** * @import { Server } from "~/components/input/https-json/common.js" */
foundation.setup({ title: "Connect HTTPS / JSON Listing | Diffuse" });
////////////////////////////////////////////// SETUP////////////////////////////////////////////
const [inputConfigurator, outputOrchestrator, sourcesOrchestrator] = await Promise.all([ foundation.configurator.input(), foundation.orchestrator.output(), foundation.orchestrator.sources(), ]);
await Promise.all([ customElements.whenDefined(inputConfigurator.localName), customElements.whenDefined(outputOrchestrator.localName), customElements.whenDefined(sourcesOrchestrator.localName),]);
////////////////////////////////////////////// UI////////////////////////////////////////////
const { setItems, setError } = setup({ title: "HTTPS + JSON", hasOutput: false,
description: html` <p> Connect to an HTTPS server that serves JSON directory listings (eg. static-web-server with <code>--directory-listing-format=json</code>). </p>
<p><small>Uses the format: <code>[{ "name": "Example folder", "type": "directory" }, { "name": "file.mp3", "type": "file" }]</code></small></p> `,
formFields: html` <label>Host* <input id="https-json-host" placeholder="music.example.com" required></label> <label>Directory <input id="https-json-dir" placeholder="/"></label> <label>Excluded directories <input id="https-json-exclude" placeholder="private, tmp"></label> <p class="caption">* Required.<br />Excluded directories are matched by name at any depth, comma-separated.</p> `,
onSubmit: () => addServer(),});
const hostInput = /** @type {HTMLInputElement} */ (document.querySelector("#https-json-host"));const dirInput = /** @type {HTMLInputElement} */ (document.querySelector("#https-json-dir"));const excludeInput = /** @type {HTMLInputElement} */ (document.querySelector("#https-json-exclude"));
////////////////////////////////////////////// REACTIVE LIST////////////////////////////////////////////
effect(() => { const inputSources = sourcesOrchestrator.sources()[SCHEME] ?? [];
/** @type {Map<string, { server: Server; uri: string }>} */ const allServers = new Map();
for (const source of inputSources) { const parsed = parseURI(source.uri); if (!parsed) continue;
const id = serverId(parsed.server); if (!allServers.has(id)) { allServers.set(id, { server: parsed.server, uri: source.uri }); } }
setItems( [...allServers.values()].map(({ server, uri }) => ({ name: server.host, detail: server.dir, isInput: true, isOutput: false, isSelectedOutput: false, isDisabled: sourcesOrchestrator.isDisabled(uri), onRemove: () => removeServer(uri), onToggleDisabled: () => sourcesOrchestrator.toggle(uri), })), );});
////////////////////////////////////////////// ACTIONS////////////////////////////////////////////
/** @param {string} uri */async function removeServer(uri) { setError(null); try { const tracks = await Output.data(outputOrchestrator.tracks); const detachedTracks = await inputConfigurator.detach({ fileUriOrScheme: uri, tracks, });
if (detachedTracks) await outputOrchestrator.tracks.save(detachedTracks); } catch (err) { setError(err instanceof Error ? err.message : "Failed to remove server"); }}
async function addServer() { const host = hostInput.value?.trim(); const dir = dirInput.value?.trim() || "/"; const exclude = (excludeInput.value ?? "").split(",").map((s) => s.trim()).filter(Boolean);
if (!host) return;
/** @type {Server} */ const server = { host, dir, exclude: exclude.length ? exclude : undefined }; const uri = buildURI(server);
const now = new Date().toISOString(); const tracksCol = outputOrchestrator.tracks.collection(); const existingTracks = tracksCol.state === "loaded" ? tracksCol.data : [];
await outputOrchestrator.tracks.save([ ...existingTracks, { $type: "sh.diffuse.output.track", id: TID.now(), createdAt: now, updatedAt: now, kind: "placeholder", uri, }, ]);}
////////////////////////////////////////////// 🚀////////////////////////////////////////////
foundation.ready();