tangled
alpha
login
or
join now
tokono.ma
/
diffuse
5
fork
atom
A music player that connects to your cloud/distributed storage.
5
fork
atom
overview
issues
4
pulls
pipelines
feat: add artwork/input component
Steven Vandevelde
6 days ago
d09361e5
4a85418a
+141
9 changed files
expand all
collapse all
unified
split
src
components
artwork
input
element.js
worker.js
configurator
input
element.js
input
https
element.js
icecast
element.js
local
element.js
opensubsonic
element.js
s3
element.js
tests
components
artwork
input
test.ts
+61
src/components/artwork/input/element.js
reviewed
···
1
1
+
import { DiffuseElement, query } from "~/common/element.js";
2
2
+
3
3
+
/**
4
4
+
* @import {ProxiedActions} from "~/common/worker.d.ts"
5
5
+
* @import {InputElement} from "~/components/input/types.d.ts"
6
6
+
* @import {Actions} from "~/components/artwork/types.d.ts"
7
7
+
*/
8
8
+
9
9
+
////////////////////////////////////////////
10
10
+
// ELEMENT
11
11
+
////////////////////////////////////////////
12
12
+
13
13
+
/**
14
14
+
* @implements {ProxiedActions<Actions>}
15
15
+
*/
16
16
+
class InputArtwork extends DiffuseElement {
17
17
+
static NAME = "diffuse/artwork/input";
18
18
+
static WORKER_URL = "components/artwork/input/worker.js";
19
19
+
20
20
+
constructor() {
21
21
+
super();
22
22
+
23
23
+
/** @type {ProxiedActions<Actions>} */
24
24
+
const p = this.workerProxy();
25
25
+
26
26
+
this.get = p.get;
27
27
+
}
28
28
+
29
29
+
// LIFECYCLE
30
30
+
31
31
+
/** @override */
32
32
+
async connectedCallback() {
33
33
+
super.connectedCallback();
34
34
+
35
35
+
/** @type {InputElement} */
36
36
+
this.input = query(this, "input-selector");
37
37
+
38
38
+
await customElements.whenDefined(this.input.localName);
39
39
+
}
40
40
+
41
41
+
// WORKERS
42
42
+
43
43
+
/**
44
44
+
* @override
45
45
+
*/
46
46
+
dependencies() {
47
47
+
if (!this.input) throw new Error("Input element not defined yet");
48
48
+
return { input: this.input };
49
49
+
}
50
50
+
}
51
51
+
52
52
+
export default InputArtwork;
53
53
+
54
54
+
////////////////////////////////////////////
55
55
+
// REGISTER
56
56
+
////////////////////////////////////////////
57
57
+
58
58
+
export const CLASS = InputArtwork;
59
59
+
export const NAME = "da-input";
60
60
+
61
61
+
customElements.define(NAME, InputArtwork);
+32
src/components/artwork/input/worker.js
reviewed
···
1
1
+
import { ostiary, rpc, workerProxy } from "~/common/worker.js";
2
2
+
3
3
+
/**
4
4
+
* @import {ActionsWithTunnel, ProxiedActions} from "~/common/worker.d.ts"
5
5
+
* @import {InputActions} from "~/components/input/types.d.ts"
6
6
+
* @import {Actions} from "~/components/artwork/types.d.ts"
7
7
+
*/
8
8
+
9
9
+
////////////////////////////////////////////
10
10
+
// ACTIONS
11
11
+
////////////////////////////////////////////
12
12
+
13
13
+
/**
14
14
+
* @type {ActionsWithTunnel<Actions>['get']}
15
15
+
*/
16
16
+
export async function get({ data: track, ports }) {
17
17
+
/** @type {ProxiedActions<InputActions>} */
18
18
+
const input = workerProxy(() => {
19
19
+
ports.input.start();
20
20
+
return ports.input;
21
21
+
});
22
22
+
23
23
+
return await input.artwork(track.uri);
24
24
+
}
25
25
+
26
26
+
////////////////////////////////////////////
27
27
+
// ⚡️
28
28
+
////////////////////////////////////////////
29
29
+
30
30
+
ostiary((context) => {
31
31
+
rpc(context, { get });
32
32
+
});
+1
src/components/configurator/input/element.js
reviewed
···
27
27
/** @type {ProxiedActions<Actions>} */
28
28
const proxy = this.workerProxy();
29
29
30
30
+
this.artwork = proxy.artwork;
30
31
this.consult = proxy.consult;
31
32
this.detach = proxy.detach;
32
33
this.groupConsult = proxy.groupConsult;
+1
src/components/input/https/element.js
reviewed
···
28
28
/** @type {ProxiedActions<InputActions>} */
29
29
this.proxy = this.workerProxy();
30
30
31
31
+
this.artwork = this.proxy.artwork;
31
32
this.consult = this.proxy.consult;
32
33
this.detach = this.proxy.detach;
33
34
this.groupConsult = this.proxy.groupConsult;
+1
src/components/input/icecast/element.js
reviewed
···
28
28
/** @type {ProxiedActions<InputActions>} */
29
29
this.proxy = this.workerProxy();
30
30
31
31
+
this.artwork = this.proxy.artwork;
31
32
this.consult = this.proxy.consult;
32
33
this.detach = this.proxy.detach;
33
34
this.groupConsult = this.proxy.groupConsult;
+1
src/components/input/local/element.js
reviewed
···
37
37
/** @type {ProxiedActions<InputActions>} */
38
38
this.proxy = this.workerProxy();
39
39
40
40
+
this.artwork = this.proxy.artwork;
40
41
this.consult = this.proxy.consult;
41
42
this.detach = this.proxy.detach;
42
43
this.groupConsult = this.proxy.groupConsult;
+1
src/components/input/opensubsonic/element.js
reviewed
···
28
28
/** @type {ProxiedActions<InputActions>} */
29
29
this.proxy = this.workerProxy();
30
30
31
31
+
this.artwork = this.proxy.artwork;
31
32
this.consult = this.proxy.consult;
32
33
this.detach = this.proxy.detach;
33
34
this.groupConsult = this.proxy.groupConsult;
+1
src/components/input/s3/element.js
reviewed
···
29
29
/** @type {ProxiedActions<InputActions & { demo: () => Demo }>} */
30
30
this.proxy = this.workerProxy();
31
31
32
32
+
this.artwork = this.proxy.artwork;
32
33
this.consult = this.proxy.consult;
33
34
this.detach = this.proxy.detach;
34
35
this.groupConsult = this.proxy.groupConsult;
+42
tests/components/artwork/input/test.ts
reviewed
···
1
1
+
import { describe, it } from "@std/testing/bdd";
2
2
+
import { expect } from "@std/expect";
3
3
+
4
4
+
import { testWeb } from "@tests/common/index.ts";
5
5
+
6
6
+
describe("components/artwork/input", () => {
7
7
+
it("delegates to input.artwork and returns null when input has no artwork", async () => {
8
8
+
const result = await testWeb(async () => {
9
9
+
const HttpsInput = await import("~/components/input/https/element.js");
10
10
+
const InputArtwork = await import(
11
11
+
"~/components/artwork/input/element.js"
12
12
+
);
13
13
+
14
14
+
const input = new HttpsInput.CLASS();
15
15
+
input.id = "test-https-input-artwork";
16
16
+
document.body.append(input);
17
17
+
18
18
+
const artwork = new InputArtwork.CLASS();
19
19
+
artwork.setAttribute("input-selector", "#test-https-input-artwork");
20
20
+
document.body.append(artwork);
21
21
+
22
22
+
await customElements.whenDefined(input.localName);
23
23
+
await customElements.whenDefined(artwork.localName);
24
24
+
25
25
+
const blob = await fetch("http://localhost:3000/testing/sample/audio.mp3")
26
26
+
.then((r) => r.blob());
27
27
+
const blobUri = URL.createObjectURL(blob);
28
28
+
29
29
+
const result = await artwork.get({
30
30
+
$type: "sh.diffuse.output.track" as const,
31
31
+
id: "input-artwork-test",
32
32
+
uri: blobUri,
33
33
+
});
34
34
+
35
35
+
URL.revokeObjectURL(blobUri);
36
36
+
37
37
+
return result ?? null;
38
38
+
});
39
39
+
40
40
+
expect(result).toBe(null);
41
41
+
});
42
42
+
});