+67
src/components/configurator/output/element.js
+67
src/components/configurator/output/element.js
···
1
+
import { DiffuseElement } from "@common/element.js";
2
+
import { computed, signal } from "@common/signal.js";
3
+
4
+
/**
5
+
* @import {ProxiedActions} from "@common/worker.d.ts"
6
+
* @import {Track} from "@definitions/types.d.ts"
7
+
* @import {OutputManager, OutputElement} from "@components/output/types.d.ts"
8
+
*/
9
+
10
+
////////////////////////////////////////////
11
+
// ELEMENT
12
+
////////////////////////////////////////////
13
+
14
+
/**
15
+
* @implements {OutputManager<Track[]>}
16
+
*/
17
+
class OutputConfigurator extends DiffuseElement {
18
+
static NAME = "diffuse/configurator/output";
19
+
static WORKER_URL = "components/configurator/output/worker.js";
20
+
21
+
constructor() {
22
+
super();
23
+
24
+
/** @type {OutputManager<Track[]>} */
25
+
const manager = {
26
+
tracks: {
27
+
collection: computed(() => {
28
+
return this.#memory.tracks.value;
29
+
}),
30
+
reload: async () => {},
31
+
save: async (newTracks) => {
32
+
this.#memory.tracks.value = newTracks;
33
+
},
34
+
state: () => "loaded",
35
+
},
36
+
};
37
+
38
+
// Assign manager properties to class
39
+
this.tracks = manager.tracks;
40
+
}
41
+
42
+
// SIGNALS
43
+
44
+
#memory = {
45
+
tracks: signal(/** @type {Track[]} */ ([])),
46
+
};
47
+
48
+
// LIFECYCLE
49
+
50
+
/**
51
+
* @override
52
+
*/
53
+
async connectedCallback() {
54
+
super.connectedCallback();
55
+
}
56
+
}
57
+
58
+
export default OutputConfigurator;
59
+
60
+
////////////////////////////////////////////
61
+
// REGISTER
62
+
////////////////////////////////////////////
63
+
64
+
export const CLASS = OutputConfigurator;
65
+
export const NAME = "dc-output";
66
+
67
+
customElements.define(NAME, CLASS);
+12
-5
src/components/output/polymorphic/indexed-db/element.js
+12
-5
src/components/output/polymorphic/indexed-db/element.js
···
4
4
/**
5
5
* @import {ProxiedActions} from "@common/worker.d.ts"
6
6
* @import {OutputManager, OutputWorkerActions} from "../../types.d.ts"
7
+
* @import {SupportedDataTypes} from "./types.d.ts"
7
8
*/
8
9
9
10
////////////////////////////////////////////
···
20
21
constructor() {
21
22
super();
22
23
23
-
/** @type {ProxiedActions<OutputWorkerActions>} */
24
+
/** @type {ProxiedActions<OutputWorkerActions<SupportedDataTypes>>} */
24
25
const p = this.workerProxy();
25
26
26
-
// Manager
27
+
/** @type {OutputManager<SupportedDataTypes>} */
27
28
const manager = outputManager({
28
29
tracks: {
29
-
empty: () => [],
30
-
get: p.getTracks,
31
-
put: p.putTracks,
30
+
empty: () => undefined,
31
+
get: () => p.get({ name: this.#cat("tracks") }),
32
+
put: (data) => p.put({ name: this.#cat("tracks"), data }),
32
33
},
33
34
});
34
35
35
36
this.tracks = manager.tracks;
37
+
}
38
+
39
+
/** @param {string} name */
40
+
#cat(name) {
41
+
const key = this.hasAttribute("key") ? this.getAttribute("key") + "/" : "";
42
+
return `${key}${name}`;
36
43
}
37
44
}
38
45
+1
src/components/output/polymorphic/indexed-db/types.d.ts
+1
src/components/output/polymorphic/indexed-db/types.d.ts
···
1
+
export type SupportedDataTypes = any;
+10
-30
src/components/output/polymorphic/indexed-db/worker.js
+10
-30
src/components/output/polymorphic/indexed-db/worker.js
···
4
4
import { ostiary, rpc } from "@common/worker.js";
5
5
6
6
/**
7
-
* @import {Track} from "@definitions/types.d.ts";
7
+
* @import {OutputWorkerActions} from "@components/output/types.d.ts";
8
+
* @import {SupportedDataTypes} from "./types.d.ts"
8
9
*/
9
10
10
11
////////////////////////////////////////////
···
12
13
////////////////////////////////////////////
13
14
14
15
/**
15
-
* @returns {Promise<Track[]>}
16
+
* @type {OutputWorkerActions<SupportedDataTypes>["get"]}
16
17
*/
17
-
export async function getTracks() {
18
-
/** @type {Track[] | null} */
19
-
const tracks = await get({ name: "tracks.json" });
20
-
return tracks ?? [];
18
+
export async function get({ name }) {
19
+
return await IDB.get(`${IDB_PREFIX}/${name}`);
21
20
}
22
21
23
22
/**
24
-
* @param {Track[]} tracks
23
+
* @type {OutputWorkerActions<SupportedDataTypes>["put"]}
25
24
*/
26
-
export async function putTracks(tracks) {
27
-
await put({ name: "tracks.json", data: tracks });
25
+
export async function put({ data, name }) {
26
+
return await IDB.set(`${IDB_PREFIX}/${name}`, data);
28
27
}
29
-
30
28
////////////////////////////////////////////
31
29
// ⚡️
32
30
////////////////////////////////////////////
33
31
34
32
ostiary((context) => {
35
33
rpc(context, {
36
-
getTracks,
37
-
putTracks,
34
+
get,
35
+
put,
38
36
});
39
37
});
40
-
41
-
////////////////////////////////////////////
42
-
// ⛔️
43
-
////////////////////////////////////////////
44
-
45
-
/**
46
-
* @param {{ name: string }} _
47
-
*/
48
-
async function get({ name }) {
49
-
return await IDB.get(`${IDB_PREFIX}/${name}`);
50
-
}
51
-
52
-
/**
53
-
* @param {{ data: any; name: string }} _
54
-
*/
55
-
async function put({ data, name }) {
56
-
return await IDB.set(`${IDB_PREFIX}/${name}`, data);
57
-
}
+3
-4
src/components/output/types.d.ts
+3
-4
src/components/output/types.d.ts
···
1
1
import type { SignalReader } from "@common/signal.d.ts";
2
-
import type { Track } from "@definitions/types.d.ts";
3
2
import type { DiffuseElement } from "@common/element.js";
4
3
5
4
export type OutputElement<Tracks> = DiffuseElement & OutputManager<Tracks>;
···
22
21
};
23
22
};
24
23
25
-
export type OutputWorkerActions = {
26
-
getTracks(): Promise<Track[]>;
27
-
putTracks(tracks: Track[]): Promise<void>;
24
+
export type OutputWorkerActions<DataType> = {
25
+
get(args: { name: string }): Promise<DataType>;
26
+
put(args: { data: DataType; name: string }): Promise<void>;
28
27
};
+65
src/components/transformer/output/base.js
+65
src/components/transformer/output/base.js
···
1
+
import { DiffuseElement, query } from "@common/element.js";
2
+
import { computed, signal } from "@common/signal.js";
3
+
4
+
/**
5
+
* @import { OutputElement, OutputManager } from "../../output/types.d.ts"
6
+
*/
7
+
8
+
/**
9
+
* @template T
10
+
*/
11
+
export class OutputTransformer extends DiffuseElement {
12
+
// SIGNALS
13
+
14
+
#output = signal(/** @type {OutputElement<T> | undefined} */ (undefined));
15
+
#outputWhenDefined = Promise.withResolvers();
16
+
17
+
output = {
18
+
whenDefined: this.#outputWhenDefined.promise,
19
+
signal: this.#output.get,
20
+
};
21
+
22
+
// LIFECYCLE
23
+
24
+
/**
25
+
* @override
26
+
*/
27
+
connectedCallback() {
28
+
super.connectedCallback();
29
+
30
+
/** @type {OutputElement<T>} */
31
+
const output = query(this, "output-selector");
32
+
33
+
// When defined
34
+
customElements.whenDefined(output.localName).then(() => {
35
+
this.#output.value = output;
36
+
this.#outputWhenDefined.resolve(null);
37
+
});
38
+
}
39
+
40
+
// MANAGER
41
+
42
+
base() {
43
+
/** @type {OutputManager<T | undefined>} */
44
+
const m = {
45
+
tracks: {
46
+
collection: computed(() => {
47
+
return this.output.signal()?.tracks?.collection();
48
+
}),
49
+
reload: () => {
50
+
return this.output.signal()?.tracks?.reload() ?? Promise.resolve();
51
+
},
52
+
save: async (newTracks) => {
53
+
if (newTracks === undefined) return;
54
+
await this.output.whenDefined;
55
+
await this.output.signal()?.tracks.save(newTracks);
56
+
},
57
+
state: computed(() =>
58
+
this.output.signal()?.tracks.state() ?? "loading"
59
+
),
60
+
},
61
+
};
62
+
63
+
return m;
64
+
}
65
+
}
+12
-39
src/components/transformer/output/refiner/default/element.js
+12
-39
src/components/transformer/output/refiner/default/element.js
···
1
-
import { DiffuseElement, query } from "@common/element.js";
2
-
import { computed, signal } from "@common/signal.js";
1
+
import { computed } from "@common/signal.js";
2
+
import { OutputTransformer } from "../../base.js";
3
3
4
4
/**
5
-
* @import { OutputElement, OutputManager } from "../../../../output/types.d.ts"
5
+
* @import { OutputManager } from "../../../../output/types.d.ts"
6
6
* @import { Track } from "@definitions/types.d.ts"
7
7
*/
8
8
9
-
class DefaultOutputRefinerTransformer extends DiffuseElement {
9
+
/**
10
+
* @extends {OutputTransformer<Track[]>}
11
+
*/
12
+
class DefaultOutputRefinerTransformer extends OutputTransformer {
10
13
constructor() {
11
14
super();
15
+
16
+
const base = this.base();
12
17
13
18
/** @type {OutputManager<Track[]>} */
14
19
const manager = {
15
20
tracks: {
21
+
...base.tracks,
16
22
collection: computed(() => {
17
-
return this.#defined.value
18
-
? this.output?.tracks?.collection() ?? []
19
-
: [];
23
+
return base.tracks.collection() ?? [];
20
24
}),
21
-
reload: () => this.output?.tracks?.reload() ?? Promise.resolve(),
22
25
save: async (newTracks) => {
23
26
const filtered = newTracks.filter((t) => !t.ephemeral);
24
-
25
-
if (!this.output) return;
26
-
27
-
await customElements.whenDefined(this.output.localName);
28
-
await this.output.tracks.save(filtered);
27
+
await base.tracks.save(filtered);
29
28
},
30
-
state: computed(() => this.output?.tracks.state() ?? "loading"),
31
29
},
32
30
};
33
31
34
32
// Assign manager properties to class
35
33
this.tracks = manager.tracks;
36
-
}
37
-
38
-
/** @type {OutputElement<Track[]> | undefined} */
39
-
output = undefined;
40
-
41
-
// SIGNALS
42
-
43
-
#defined = signal(false);
44
-
45
-
// LIFECYCLE
46
-
47
-
/**
48
-
* @override
49
-
*/
50
-
connectedCallback() {
51
-
super.connectedCallback();
52
-
53
-
/** @type {OutputElement<Track[]>} */
54
-
const output = query(this, "output-selector");
55
-
this.output = output;
56
-
57
-
// When defined
58
-
customElements.whenDefined(this.output.localName).then(
59
-
() => this.#defined.value = true,
60
-
);
61
34
}
62
35
}
63
36
+12
-43
src/components/transformer/output/string/json/element.js
+12
-43
src/components/transformer/output/string/json/element.js
···
1
-
import { DiffuseElement, query } from "@common/element.js";
2
-
import { computed, signal } from "@common/signal.js";
1
+
import { computed } from "@common/signal.js";
2
+
import { OutputTransformer } from "../../base.js";
3
3
4
4
/**
5
-
* @import { OutputElement, OutputManager } from "../../../../output/types.d.ts"
5
+
* @import { OutputManager } from "../../../../output/types.d.ts"
6
6
* @import { Track } from "@definitions/types.d.ts"
7
7
*/
8
8
9
-
class JsonStringOutputTransformer extends DiffuseElement {
9
+
/**
10
+
* @extends {OutputTransformer<string>}
11
+
*/
12
+
class JsonStringOutputTransformer extends OutputTransformer {
10
13
constructor() {
11
14
super();
12
15
16
+
const base = this.base();
17
+
13
18
/** @type {OutputManager<Track[]>} */
14
19
const manager = {
15
20
tracks: {
21
+
...base.tracks,
16
22
collection: computed(() => {
17
-
const json = this.#defined.value
18
-
? this.output?.tracks?.collection() ?? []
19
-
: [];
20
-
21
-
// In addition to the above, Some polymorphic outputs
22
-
// use an empty array as the default return value.
23
-
if (Array.isArray(json)) return json;
23
+
const json = base.tracks.collection() ?? "[]";
24
24
25
25
// Try parsing JSON
26
26
try {
···
32
32
return [];
33
33
}
34
34
}),
35
-
reload: () => this.output?.tracks?.reload() ?? Promise.resolve(),
36
35
save: async (newTracks) => {
37
36
const json = JSON.stringify(newTracks);
38
-
39
-
if (!this.output) return;
40
-
41
-
await customElements.whenDefined(this.output.localName);
42
-
await this.output.tracks.save(json);
37
+
await base.tracks.save(json);
43
38
},
44
-
state: computed(() => this.output?.tracks?.state() ?? "loading"),
45
39
},
46
40
};
47
41
48
42
// Assign manager properties to class
49
43
this.tracks = manager.tracks;
50
-
}
51
-
52
-
/** @type {OutputElement<string> | undefined} */
53
-
output = undefined;
54
-
55
-
// SIGNALS
56
-
57
-
#defined = signal(false);
58
-
59
-
// LIFECYCLE
60
-
61
-
/**
62
-
* @override
63
-
*/
64
-
connectedCallback() {
65
-
super.connectedCallback();
66
-
67
-
/** @type {OutputElement<string>} */
68
-
const output = query(this, "output-selector");
69
-
this.output = output;
70
-
71
-
// When defined
72
-
customElements.whenDefined(this.output.localName).then(
73
-
() => this.#defined.value = true,
74
-
);
75
44
}
76
45
}
77
46
+1
-1
src/themes/webamp/index.js
+1
-1
src/themes/webamp/index.js
···
1
-
// import "@components/orchestrator/process-tracks/element.js";
1
+
import "@components/orchestrator/process-tracks/element.js";
2
2
import "@components/orchestrator/queue-tracks/element.js";
3
3
import "@components/input/opensubsonic/element.js";
4
4
import "@components/input/s3/element.js";