forked from
tokono.ma/diffuse
A music player that connects to your cloud/distributed storage.
1import { computed } from "~/common/signal.js";
2import { OutputTransformer } from "../../base.js";
3import { defineElement } from "~/common/element.js";
4
5/**
6 * @import { OutputManagerDeputy } from "~/components/output/types.d.ts"
7 * @import { Facet, PlaylistItem, Setting, Track } from "~/definitions/types.d.ts"
8 */
9
10/**
11 * @extends {OutputTransformer<Uint8Array>}
12 */
13class JsonStringOutputTransformer extends OutputTransformer {
14 constructor() {
15 super();
16
17 const base = this.base();
18
19 /** @type {OutputManagerDeputy} */
20 const manager = {
21 facets: {
22 ...base.facets,
23 collection: computed(() => {
24 const col = base.facets.collection();
25 if (col.state !== "loaded") return col;
26 /** @type {Facet[]} */
27 const data = parseArray(col.data);
28 return { state: "loaded", data };
29 }),
30 save: async (newFacets) => {
31 const json = JSON.stringify(newFacets);
32 const encoder = new TextEncoder();
33 const bytes = encoder.encode(json);
34 await base.facets.save(bytes);
35 },
36 },
37 playlistItems: {
38 ...base.playlistItems,
39 collection: computed(() => {
40 const col = base.playlistItems.collection();
41 if (col.state !== "loaded") return col;
42 /** @type {PlaylistItem[]} */
43 const data = parseArray(col.data);
44 return { state: "loaded", data };
45 }),
46 save: async (newPlaylistItems) => {
47 const json = JSON.stringify(newPlaylistItems);
48 const encoder = new TextEncoder();
49 const bytes = encoder.encode(json);
50 await base.playlistItems.save(bytes);
51 },
52 },
53 settings: {
54 ...base.settings,
55 collection: computed(() => {
56 const col = base.settings.collection();
57 if (col.state !== "loaded") return col;
58 /** @type {Setting[]} */
59 const data = parseArray(col.data);
60 return { state: "loaded", data };
61 }),
62 save: async (newSettings) => {
63 const json = JSON.stringify(newSettings);
64 const encoder = new TextEncoder();
65 const bytes = encoder.encode(json);
66 await base.settings.save(bytes);
67 },
68 },
69 tracks: {
70 ...base.tracks,
71 collection: computed(() => {
72 const col = base.tracks.collection();
73 if (col.state !== "loaded") return col;
74 /** @type {Track[]} */
75 const data = parseArray(col.data);
76 return { state: "loaded", data };
77 }),
78 save: async (newTracks) => {
79 const json = JSON.stringify(newTracks);
80 const encoder = new TextEncoder();
81 const bytes = encoder.encode(json);
82 await base.tracks.save(bytes);
83 },
84 },
85
86 // Other
87 ready: base.ready,
88 };
89
90 // Assign manager properties to class
91 this.facets = manager.facets;
92 this.playlistItems = manager.playlistItems;
93 this.settings = manager.settings;
94 this.tracks = manager.tracks;
95 this.ready = manager.ready;
96 }
97}
98
99/**
100 * @param {Uint8Array | string | undefined} data
101 */
102function parseArray(data) {
103 let json;
104
105 if (data instanceof Uint8Array) {
106 const decoder = new TextDecoder();
107 json = decoder.decode(data);
108 } else if (data === undefined) {
109 return [];
110 } else {
111 json = data;
112 }
113
114 try {
115 return JSON.parse(json);
116 } catch (err) {
117 console.error(err);
118 return [];
119 }
120}
121
122export default JsonStringOutputTransformer;
123
124////////////////////////////////////////////
125// REGISTER
126////////////////////////////////////////////
127
128export const CLASS = JsonStringOutputTransformer;
129export const NAME = "dtob-json";
130
131defineElement(NAME, CLASS);