diff --git a/deno.lock b/deno.lock index 155171d..154a152 100644 --- a/deno.lock +++ b/deno.lock @@ -21,14 +21,17 @@ ], "packageJson": { "dependencies": [ + "npm:@atcute/cid@^2.2.2", "npm:@picocss/pico@^2.1.1", "npm:astro-purgecss@^5.2.2", "npm:astro-scope@^3.0.1", "npm:astro@^5.7.4", + "npm:fast-uri@^3.0.6", "npm:iconoir@^7.11.0", "npm:idb-keyval@^6.2.1", "npm:native-file-system-adapter@^3.0.1", "npm:purgecss@^7.0.2", + "npm:query-string@^9.1.2", "npm:sass@^1.87.0", "npm:throttle-debounce@^5.0.2", "npm:xxh32@^2.0.5" diff --git a/package-lock.json b/package-lock.json index c3fccec..43d60ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,11 +5,14 @@ "packages": { "": { "dependencies": { + "@atcute/cid": "^2.2.2", "@picocss/pico": "^2.1.1", "@web-applets/sdk": "file:../../unternet-co/web-applets/sdk/", + "fast-uri": "^3.0.6", "iconoir": "^7.11.0", "idb-keyval": "^6.2.1", "native-file-system-adapter": "^3.0.1", + "query-string": "^9.1.2", "spellcaster": "gordonbrander/spellcaster#1613e5e3b7f202cfe57f37ea7c637ec83588a297", "throttle-debounce": "^5.0.2", "xxh32": "^2.0.5" @@ -104,6 +107,28 @@ "node": "^18.17.1 || ^20.3.0 || >=22.0.0" } }, + "node_modules/@atcute/cid": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@atcute/cid/-/cid-2.2.2.tgz", + "integrity": "sha512-deAGMqLAyplt7eIukhkjlsGubvrcMrtXkDKlUYZDo4WUdL7hSjBywtPXf6SbMK+Mjvst7l2+83OqTcY5AuuxtA==", + "dependencies": { + "@atcute/multibase": "^1.1.3", + "@atcute/uint8array": "^1.0.1" + } + }, + "node_modules/@atcute/multibase": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@atcute/multibase/-/multibase-1.1.3.tgz", + "integrity": "sha512-vQQO0tDuQPguBvHdgV3ryn7R8U6beQ50KA/juYm+dCeT/3hOK2stMbX+IaW8JEuwkT5lJsU8wDIOicQT4mB7Ag==", + "dependencies": { + "@atcute/uint8array": "^1.0.1" + } + }, + "node_modules/@atcute/uint8array": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@atcute/uint8array/-/uint8array-1.0.1.tgz", + "integrity": "sha512-AAnlFKyfDRgb9GNZJbhQ6OuMhbmNPirQyapb8KnmcEhxQZ3+tt+4NcwqekEegY4MpNqSTYeeTdyxq0wGZv1JHg==" + }, "node_modules/@babel/helper-string-parser": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", @@ -2398,6 +2423,14 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/decode-uri-component": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz", + "integrity": "sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==", + "engines": { + "node": ">=14.16" + } + }, "node_modules/defu": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", @@ -2605,6 +2638,21 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, "node_modules/fdir": { "version": "6.4.4", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", @@ -2655,6 +2703,17 @@ "node": ">=8" } }, + "node_modules/filter-obj": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz", + "integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/flattie": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", @@ -4478,6 +4537,22 @@ "purgecss": "bin/purgecss.js" } }, + "node_modules/query-string": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-9.1.2.tgz", + "integrity": "sha512-s3UlTyjxRux4KjwWaJsjh1Mp8zoCkSGKirbD9H89pEM9UOZsfpRZpdfzvsy2/mGlLfC3NnYVpy2gk7jXITHEtA==", + "dependencies": { + "decode-uri-component": "^0.4.1", + "filter-obj": "^5.1.0", + "split-on-first": "^3.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/radix3": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", @@ -4950,6 +5025,17 @@ "signal-polyfill": "^0.2.0" } }, + "node_modules/split-on-first": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-3.0.0.tgz", + "integrity": "sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", diff --git a/package.json b/package.json index 2e32294..b463cd8 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,13 @@ { "dependencies": { + "@atcute/cid": "^2.2.2", "@picocss/pico": "^2.1.1", "@web-applets/sdk": "file:../../unternet-co/web-applets/sdk/", + "fast-uri": "^3.0.6", "iconoir": "^7.11.0", "idb-keyval": "^6.2.1", "native-file-system-adapter": "^3.0.1", + "query-string": "^9.1.2", "spellcaster": "gordonbrander/spellcaster#1613e5e3b7f202cfe57f37ea7c637ec83588a297", "throttle-debounce": "^5.0.2", "xxh32": "^2.0.5" diff --git a/src/pages/core/types.d.ts b/src/pages/core/types.d.ts index 67fa785..9792832 100644 --- a/src/pages/core/types.d.ts +++ b/src/pages/core/types.d.ts @@ -1,35 +1,17 @@ /* OUTPUT */ export interface Output { - sources: Source[]; tracks: Track[]; } export type OutputGetter = ({ name }: { name: string }) => Promise; export type OutputSetter = ({ data, name }: { data: Uint8Array; name: string }) => Promise; -/* SOURCES */ -// TODO: Rename to input? - -export interface Source> { - id: string; - meta: Meta; - - // NOTE: This is associated with a data input applet. - // For example, `diffuse.sh/storage/amazon-s3/` - // - // This association is needed to, for example, - // optionally translate permanent URIs into usable - // URLs in order actually play the audio track. - appletURI: string; -} - /* TRACKS */ export interface Track { id: string; - sourceId: string; - tags: Tags; + tags?: Tags; // NOTE: This is a "semi-permanent" URI. // diff --git a/src/pages/engine/audio/_manifest.json b/src/pages/engine/audio/_manifest.json index 655c0bb..efcf2c6 100644 --- a/src/pages/engine/audio/_manifest.json +++ b/src/pages/engine/audio/_manifest.json @@ -59,19 +59,15 @@ "type": "array", "description": "The tracks we want to render.", "items": { - "anyOf": [ - { - "type": "object", - "properties": { - "id": { "type": "string" }, - "isPreload": { "type": "boolean" }, - "mimeType": { "type": "string" }, - "progress": { "type": "number" }, - "url": { "type": "string" } - }, - "required": ["id", "url"] - } - ] + "type": "object", + "properties": { + "id": { "type": "string" }, + "isPreload": { "type": "boolean" }, + "mimeType": { "type": "string" }, + "progress": { "type": "number" }, + "url": { "type": "string" } + }, + "required": ["id", "url"] } } }, diff --git a/src/pages/input/native-fs/_applet.astro b/src/pages/input/native-fs/_applet.astro new file mode 100644 index 0000000..1cb65fc --- /dev/null +++ b/src/pages/input/native-fs/_applet.astro @@ -0,0 +1,155 @@ + diff --git a/src/pages/input/native-fs/_manifest.json b/src/pages/input/native-fs/_manifest.json new file mode 100644 index 0000000..597ce35 --- /dev/null +++ b/src/pages/input/native-fs/_manifest.json @@ -0,0 +1,34 @@ +{ + "name": "diffuse/input/native-fs", + "title": "Diffuse Input | Native File System", + "entrypoint": "index.html", + "input_properties": { + "scheme": "file+local" + }, + "actions": { + "list": { + "title": "List", + "description": "List tracks.", + "params_schema": { + "type": "object", + "properties": { + "tracks": { + "type": "array", + "description": "A list of (cached) tracks with an uri matching the scheme", + "items": { + "type": "object" + } + } + } + } + }, + "mount": { + "title": "Mount", + "description": "Prepare for usage." + }, + "unmount": { + "title": "Unmount", + "description": "Callback after usage." + } + } +} diff --git a/src/pages/input/native-fs/index.astro b/src/pages/input/native-fs/index.astro new file mode 100644 index 0000000..dfb1e1a --- /dev/null +++ b/src/pages/input/native-fs/index.astro @@ -0,0 +1,9 @@ +--- +import Layout from "@layouts/applet.astro"; +import Applet from "./_applet.astro"; +import { title } from "./_manifest.json"; +--- + + + + diff --git a/src/pages/output/native-fs/_applet.astro b/src/pages/output/native-fs/_applet.astro index 53726d1..23ffed8 100644 --- a/src/pages/output/native-fs/_applet.astro +++ b/src/pages/output/native-fs/_applet.astro @@ -17,7 +17,7 @@ // ACTIONS //////////////////////////////////////////// const get: OutputGetter = async ({ name }) => { - const handle = await IDB.get(IDB_DEVICE_KEY); + const handle: FileSystemDirectoryHandle | null = (await IDB.get(IDB_DEVICE_KEY)) ?? null; if (!handle) throw new Error("Storage not configured properly, handle not found."); try { @@ -34,7 +34,7 @@ }; const put: OutputSetter = async ({ data, name }) => { - const handle = await IDB.get(IDB_DEVICE_KEY); + const handle: FileSystemDirectoryHandle | null = (await IDB.get(IDB_DEVICE_KEY)) ?? null; if (!handle) throw new Error("Storage not configured properly, handle not found."); const fileHandle = await handle.getFileHandle(name, { create: true }); const stream = await fileHandle.createWritable(); diff --git a/src/scripts/inputs/common.ts b/src/scripts/inputs/common.ts new file mode 100644 index 0000000..167a7b2 --- /dev/null +++ b/src/scripts/inputs/common.ts @@ -0,0 +1,3 @@ +export function isAudioFile(filename: string) { + return filename.match(/\.(flac|m4a|mp3|mp4|ogg|opus|wav|webm)$/); +} diff --git a/src/scripts/themes/pilot/index.ts b/src/scripts/themes/pilot/index.ts index 733dc98..212accb 100644 --- a/src/scripts/themes/pilot/index.ts +++ b/src/scripts/themes/pilot/index.ts @@ -23,6 +23,10 @@ const engine = { queue: await applet("../../engine/queue"), }; +const input = { + nativeFs: await applet("../../input/native-fs"), +}; + const _orchestrator = { queue: await applet("../../orchestrator/single-queue"), output: await applet("../../orchestrator/output-management"), @@ -91,3 +95,10 @@ reactive( } }, ); + +// TODO: +document.onclick = async () => { + await input.nativeFs.sendAction("mount"); + await input.nativeFs.sendAction("list"); + console.log(input.nativeFs.data); +}; diff --git a/src/styles/themes/pilot/index.css b/src/styles/themes/pilot/index.css index 265a3b3..2ad46e2 100644 --- a/src/styles/themes/pilot/index.css +++ b/src/styles/themes/pilot/index.css @@ -52,7 +52,9 @@ iframe { ***********************************/ iframe[src*="/configurator/"], iframe[src*="/engine/"], -iframe[src*="/orchestrator/"] { +iframe[src*="/input/"], +iframe[src*="/orchestrator/"], +iframe[src*="/output/"] { height: 0; left: 110vw; opacity: 0;