A music player that connects to your cloud/distributed storage.
1import { computed } from "~/common/signal.js";
2import { OutputTransformer } from "~/components/transformer/output/base.js";
3
4/**
5 * @import {PlaylistItem, Track} from "~/definitions/types.d.ts"
6 */
7
8////////////////////////////////////////////
9// ELEMENT
10////////////////////////////////////////////
11
12class PathCollectionsOrchestrator extends OutputTransformer {
13 static NAME = "diffuse/orchestrator/path-collections";
14
15 constructor() {
16 super();
17
18 const base = this.base();
19
20 const ephemeralItems = computed(() => {
21 const col = base.tracks.collection();
22 return createEphemeralItems(col.state === "loaded" ? col.data : []);
23 });
24
25 this.facets = base.facets;
26 this.playlistItems = {
27 ...base.playlistItems,
28 collection: computed(() => {
29 const col = base.playlistItems.collection();
30 if (col.state !== "loaded") return col;
31 return { state: "loaded", data: [...col.data, ...ephemeralItems()] };
32 }),
33 /** @type {(typeof base.playlistItems.save)} */
34 save: async (items) => {
35 await base.playlistItems.save(
36 (items || []).filter((p) => !p.ephemeral),
37 );
38 },
39 };
40 this.tracks = base.tracks;
41
42 this.ready = base.ready;
43 }
44}
45
46/**
47 * @param {Track[]} tracks
48 * @returns {PlaylistItem[]}
49 */
50function createEphemeralItems(tracks) {
51 /** @type {PlaylistItem[]} */
52 const items = [];
53
54 /** @type {Set<string>} */
55 const segmentsAdded = new Set();
56
57 tracks.forEach((track) => {
58 const segment = pathSegment(track.uri);
59 if (!segment) return;
60
61 if (segmentsAdded.has(segment)) return;
62
63 /** @type {PlaylistItem} */
64 const item = {
65 $type: "sh.diffuse.output.playlistItem",
66 id: `path-collections/${track.id}`,
67 playlist: segment,
68 criteria: [{ field: "uri", value: /** @type {any} */ (track.uri) }],
69 ephemeral: true,
70 };
71
72 items.push(item);
73 segmentsAdded.add(segment);
74 });
75
76 return items;
77}
78
79/**
80 * @param {string} uri
81 * @returns {string | undefined}
82 */
83function pathSegment(uri) {
84 try {
85 const url = new URL(uri);
86 const segment = url.pathname.split("/").filter(Boolean)[0];
87 return segment || undefined;
88 } catch {
89 return undefined;
90 }
91}
92
93export default PathCollectionsOrchestrator;
94
95////////////////////////////////////////////
96// REGISTER
97////////////////////////////////////////////
98
99export const CLASS = PathCollectionsOrchestrator;
100export const NAME = "do-path-collections";
101
102customElements.define(NAME, CLASS);