···
18
18
"iconoir": "^7.11.0",
19
19
"idb-keyval": "^6.2.1",
20
20
"music-metadata": "^11.2.3",
21
21
-
"native-file-system-adapter": "^3.0.1",
22
21
"query-string": "^9.1.2",
23
22
"spellcaster": "^6.0.0",
24
23
"subsonic-api": "^3.1.2",
···
344
344
};
345
345
346
346
const _orchestrator = {
347
347
-
inputCache: await applet("../../../orchestrator/input-cache"),
348
347
queueAudio: await applet("../../../orchestrator/queue-audio", {
349
348
groupId: groupId(),
350
349
}),
350
350
+
351
351
+
// When using the `main` group, load additional orchestrators:
352
352
+
inputCache:
353
353
+
context.groupId === undefined || context.groupId === "main"
354
354
+
? await applet("../../../orchestrator/input-cache")
355
355
+
: undefined,
351
356
queueTracks:
352
357
context.groupId === undefined || context.groupId === "main"
353
358
? applet("../../../orchestrator/queue-tracks")
···
1
1
<script>
2
2
-
import * as IDB from "idb-keyval";
3
3
-
2
2
+
import type { Actions } from "@scripts/output/indexed-db/worker";
4
3
import type { ManagedOutput, Track } from "@applets/core/types.d.ts";
5
4
import { register } from "@scripts/applet/common";
5
5
+
import { endpoint } from "@scripts/common";
6
6
import { INITIAL_MANAGED_OUTPUT, outputManager } from "@scripts/output/common";
7
7
-
import { jsonDecode, jsonEncode } from "@scripts/common";
8
7
9
8
////////////////////////////////////////////
10
9
// SETUP
11
10
////////////////////////////////////////////
12
12
-
const IDB_PREFIX = "@applets/output/indexed-db";
11
11
+
const worker = endpoint<Actions>(
12
12
+
new Worker("../../../scripts/output/indexed-db/worker", { type: "module" }),
13
13
+
);
13
14
15
15
+
// Register applet
14
16
const context = register<ManagedOutput>();
17
17
+
18
18
+
// Initial state
15
19
context.data = INITIAL_MANAGED_OUTPUT;
16
20
17
21
// Output manager
···
19
23
context,
20
24
tracks: {
21
25
async get() {
22
22
-
const encoded = await get({ name: "tracks.json" });
23
23
-
if (!encoded) return [];
24
24
-
return jsonDecode<Track[]>(encoded);
26
26
+
return worker.call.getTracks();
25
27
},
26
28
27
29
async put(tracks: Track[]) {
28
28
-
const data = jsonEncode(tracks);
29
29
-
await put({ name: "tracks.json", data });
30
30
+
return worker.call.putTracks(tracks);
30
31
},
31
32
},
32
33
});
···
41
42
42
43
context.setActionHandler("mount", mount);
43
44
context.setActionHandler("unmount", unmount);
44
44
-
45
45
-
////////////////////////////////////////////
46
46
-
// 🛠️
47
47
-
////////////////////////////////////////////
48
48
-
async function get({ name }: { name: string }) {
49
49
-
return await IDB.get(`${IDB_PREFIX}/${name}`);
50
50
-
}
51
51
-
52
52
-
async function put({ data, name }: { data: Uint8Array; name: string }) {
53
53
-
return await IDB.set(`${IDB_PREFIX}/${name}`, data);
54
54
-
}
55
45
</script>
···
1
1
<script>
2
2
+
import type { Actions } from "@scripts/output/native-fs/worker";
2
3
import * as IDB from "idb-keyval";
3
3
-
import type * as FSA from "wicg-file-system-access";
4
4
+
import "wicg-file-system-access";
4
5
5
6
import type { ManagedOutput, Track } from "@applets/core/types";
6
7
import { register } from "@scripts/applet/common";
7
8
import { INITIAL_MANAGED_OUTPUT, outputManager } from "@scripts/output/common";
8
8
-
import { jsonDecode, jsonEncode } from "@scripts/common";
9
9
+
import { endpoint, jsonDecode, jsonEncode } from "@scripts/common";
10
10
+
import { IDB_DEVICE_KEY } from "@scripts/output/native-fs/constants";
9
11
10
12
////////////////////////////////////////////
11
13
// SETUP
12
14
////////////////////////////////////////////
13
13
-
const IDB_PREFIX = "@applets/output/native-fs";
14
14
-
const IDB_DEVICE_KEY = `${IDB_PREFIX}/device`;
15
15
+
const worker = endpoint<Actions>(
16
16
+
new Worker("../../../scripts/output/native-fs/worker", { type: "module" }),
17
17
+
);
15
18
19
19
+
// Register applet
16
20
const context = register<ManagedOutput>();
21
21
+
22
22
+
// Initial state
17
23
context.data = INITIAL_MANAGED_OUTPUT;
18
24
19
25
// Output manager
···
26
32
27
33
tracks: {
28
34
async get() {
29
29
-
const encoded = await get({ name: "tracks.json" });
30
30
-
if (!encoded) return [];
31
31
-
return jsonDecode<Track[]>(encoded);
35
35
+
return worker.call.getTracks();
32
36
},
33
37
34
38
async put(tracks: Track[]) {
35
35
-
const data = jsonEncode(tracks);
36
36
-
await put({ name: "tracks.json", data });
39
39
+
return worker.call.putTracks(tracks);
37
40
},
38
41
},
39
42
});
···
65
68
66
69
context.setActionHandler("mount", mount);
67
70
context.setActionHandler("unmount", unmount);
68
68
-
69
69
-
////////////////////////////////////////////
70
70
-
// 🛠️
71
71
-
////////////////////////////////////////////
72
72
-
async function get({ name }: { name: string }) {
73
73
-
const handle: FileSystemDirectoryHandle | null = (await IDB.get(IDB_DEVICE_KEY)) ?? null;
74
74
-
if (!handle) throw new Error("Storage not configured properly, handle not found.");
75
75
-
76
76
-
try {
77
77
-
const fileHandle = await handle.getFileHandle(name);
78
78
-
const file = await fileHandle.getFile();
79
79
-
const data = await file.bytes();
80
80
-
return data;
81
81
-
} catch (err) {
82
82
-
return undefined;
83
83
-
}
84
84
-
}
85
85
-
86
86
-
async function put({ data, name }: { data: Uint8Array; name: string }) {
87
87
-
const handle: FileSystemDirectoryHandle | null = (await IDB.get(IDB_DEVICE_KEY)) ?? null;
88
88
-
if (!handle) throw new Error("Storage not configured properly, handle not found.");
89
89
-
const fileHandle = await handle.getFileHandle(name, { create: true });
90
90
-
const stream = await fileHandle.createWritable();
91
91
-
await stream.write(data);
92
92
-
await stream.close();
93
93
-
}
94
71
</script>
···
1
1
+
export const IDB_PREFIX = "@applets/output/indexed-db";
···
1
1
+
import * as IDB from "idb-keyval";
2
2
+
3
3
+
import { expose, jsonDecode, jsonEncode } from "@scripts/common";
4
4
+
import type { Track } from "@applets/core/types";
5
5
+
import { IDB_PREFIX } from "./constants";
6
6
+
7
7
+
////////////////////////////////////////////
8
8
+
// ACTIONS
9
9
+
////////////////////////////////////////////
10
10
+
const actions = expose({
11
11
+
getTracks,
12
12
+
putTracks,
13
13
+
});
14
14
+
15
15
+
export type Actions = typeof actions;
16
16
+
17
17
+
// Actions
18
18
+
19
19
+
async function getTracks() {
20
20
+
const encoded = await get({ name: "tracks.json" });
21
21
+
if (!encoded) return [];
22
22
+
return jsonDecode<Track[]>(encoded);
23
23
+
}
24
24
+
25
25
+
async function putTracks(tracks: Track[]) {
26
26
+
const data = jsonEncode(tracks);
27
27
+
await put({ name: "tracks.json", data });
28
28
+
}
29
29
+
30
30
+
////////////////////////////////////////////
31
31
+
// 🛠️
32
32
+
////////////////////////////////////////////
33
33
+
34
34
+
async function get({ name }: { name: string }) {
35
35
+
return await IDB.get(`${IDB_PREFIX}/${name}`);
36
36
+
}
37
37
+
38
38
+
async function put({ data, name }: { data: Uint8Array; name: string }) {
39
39
+
return await IDB.set(`${IDB_PREFIX}/${name}`, data);
40
40
+
}
···
1
1
+
export const IDB_PREFIX = "@applets/output/native-fs";
2
2
+
export const IDB_DEVICE_KEY = `${IDB_PREFIX}/device`;
···
1
1
+
import * as IDB from "idb-keyval";
2
2
+
3
3
+
import { expose, jsonDecode, jsonEncode } from "@scripts/common";
4
4
+
import type { Track } from "@applets/core/types";
5
5
+
import { IDB_DEVICE_KEY, IDB_PREFIX } from "./constants";
6
6
+
7
7
+
////////////////////////////////////////////
8
8
+
// ACTIONS
9
9
+
////////////////////////////////////////////
10
10
+
const actions = expose({
11
11
+
getTracks,
12
12
+
putTracks,
13
13
+
});
14
14
+
15
15
+
export type Actions = typeof actions;
16
16
+
17
17
+
// Actions
18
18
+
19
19
+
async function getTracks() {
20
20
+
const encoded = await get({ name: "tracks.json" });
21
21
+
if (!encoded) return [];
22
22
+
return jsonDecode<Track[]>(encoded);
23
23
+
}
24
24
+
25
25
+
async function putTracks(tracks: Track[]) {
26
26
+
const data = jsonEncode(tracks);
27
27
+
await put({ name: "tracks.json", data });
28
28
+
}
29
29
+
30
30
+
////////////////////////////////////////////
31
31
+
// 🛠️
32
32
+
////////////////////////////////////////////
33
33
+
34
34
+
async function get({ name }: { name: string }) {
35
35
+
const handle: FileSystemDirectoryHandle | null = (await IDB.get(IDB_DEVICE_KEY)) ?? null;
36
36
+
if (!handle) throw new Error("Storage not configured properly, handle not found.");
37
37
+
38
38
+
try {
39
39
+
const fileHandle = await handle.getFileHandle(name);
40
40
+
const file = await fileHandle.getFile();
41
41
+
const data = await file.bytes();
42
42
+
return data;
43
43
+
} catch (err) {
44
44
+
return undefined;
45
45
+
}
46
46
+
}
47
47
+
48
48
+
async function put({ data, name }: { data: Uint8Array; name: string }) {
49
49
+
const handle: FileSystemDirectoryHandle | null = (await IDB.get(IDB_DEVICE_KEY)) ?? null;
50
50
+
if (!handle) throw new Error("Storage not configured properly, handle not found.");
51
51
+
const fileHandle = await handle.getFileHandle(name, { create: true });
52
52
+
const stream = await fileHandle.createWritable();
53
53
+
await stream.write(data);
54
54
+
await stream.close();
55
55
+
}