forked from
tokono.ma/diffuse
A music player that connects to your cloud/distributed storage.
1import * as TID from "@atcute/tid";
2import { html } from "lit-html";
3
4import * as Output from "~/common/output.js";
5import { SCHEME } from "~/components/input/https/constants.js";
6import { parseURI } from "~/components/input/https/common.js";
7import { effect } from "~/common/signal.js";
8import foundation from "~/common/foundation.js";
9
10import { setup } from "~/facets/connect/common.js";
11
12foundation.setup({ title: "Connect HTTPS | Diffuse" });
13
14////////////////////////////////////////////
15// SETUP
16////////////////////////////////////////////
17
18const [inputConfigurator, outputOrchestrator, sourcesOrchestrator] =
19 await Promise.all([
20 foundation.configurator.input(),
21 foundation.orchestrator.output(),
22 foundation.orchestrator.sources(),
23 ]);
24
25await Promise.all([
26 customElements.whenDefined(inputConfigurator.localName),
27 customElements.whenDefined(outputOrchestrator.localName),
28 customElements.whenDefined(sourcesOrchestrator.localName),
29]);
30
31////////////////////////////////////////////
32// UI
33////////////////////////////////////////////
34
35const { setItems, setError } = setup({
36 title: "HTTPS",
37 hasOutput: false,
38
39 description: html`
40 <p>Add HTTPS urls as audio sources.</p>
41 `,
42
43 formFields: html`
44 <label>URL <input id="https-url" type="url" placeholder="https://example.com/audio.mp3" required></label>
45 `,
46
47 onSubmit: () => addUrl(),
48});
49
50const urlInput = /** @type {HTMLInputElement} */ (document.querySelector("#https-url"));
51
52////////////////////////////////////////////
53// REACTIVE LIST
54////////////////////////////////////////////
55
56effect(() => {
57 const inputSources = sourcesOrchestrator.sources()[SCHEME] ?? [];
58
59 setItems(
60 inputSources.map((source) => {
61 const parsed = parseURI(source.uri);
62 return {
63 name: source.uri,
64 detail: parsed?.host ?? "",
65 isInput: true,
66 isOutput: false,
67 isSelectedOutput: false,
68 onRemove: () => removeUrl(source.uri),
69 };
70 }),
71 );
72});
73////////////////////////////////////////////
74// ACTIONS
75////////////////////////////////////////////
76
77/** @param {string} uri */
78async function removeUrl(uri) {
79 setError(null);
80 try {
81 const tracks = await Output.data(outputOrchestrator.tracks);
82 const detachedTracks = await inputConfigurator.detach({
83 fileUriOrScheme: uri,
84 tracks,
85 });
86
87 if (detachedTracks) await outputOrchestrator.tracks.save(detachedTracks);
88 } catch (err) {
89 setError(err instanceof Error ? err.message : "Failed to remove URL");
90 }
91}
92
93async function addUrl() {
94 const url = urlInput.value?.trim();
95 if (!url) return;
96
97 const now = new Date().toISOString();
98 const tracksCol = outputOrchestrator.tracks.collection();
99 const existingTracks = tracksCol.state === "loaded" ? tracksCol.data : [];
100
101 await outputOrchestrator.tracks.save([
102 ...existingTracks,
103 {
104 $type: "sh.diffuse.output.track",
105 id: TID.now(),
106 createdAt: now,
107 updatedAt: now,
108 kind: "placeholder",
109 uri: url,
110 },
111 ]);
112}
113
114////////////////////////////////////////////
115// 🚀
116////////////////////////////////////////////
117
118foundation.ready();