From 97210914c939518681841d831e1f248408894eb4 Mon Sep 17 00:00:00 2001 From: ICheered Date: Fri, 4 Aug 2023 17:49:09 +0200 Subject: [PATCH] Improve folder structure --- .gitattributes | 2 + Makefile | 2 +- {ui => build}/libapi.mjs | 0 {ui => build}/libapi.wasm | Bin {ui => build}/libapi.worker.js | 0 {ui => examples/preact}/.prettierrc | 0 examples/preact/build | 1 + {ui => examples/preact}/capture-button.js | 0 {ui => examples/preact}/index-fallback.js | 0 {ui => examples/preact}/index.html | 0 {ui => examples/preact}/index.js | 6 +- {ui => examples/preact}/ops.js | 8 +- {ui => examples/preact}/preview.js | 0 {ui => examples/preact}/serve.json | 0 {ui => examples/preact}/widget.js | 2 +- package.json | 10 +- src/camera.js | 116 ++++++++++++++++++++++ {ui => src}/libapi.mjs.d.ts | 0 ui/camera.js | 116 ---------------------- 19 files changed, 134 insertions(+), 129 deletions(-) create mode 100644 .gitattributes rename {ui => build}/libapi.mjs (100%) rename {ui => build}/libapi.wasm (100%) rename {ui => build}/libapi.worker.js (100%) rename {ui => examples/preact}/.prettierrc (100%) create mode 120000 examples/preact/build rename {ui => examples/preact}/capture-button.js (100%) rename {ui => examples/preact}/index-fallback.js (100%) rename {ui => examples/preact}/index.html (100%) rename {ui => examples/preact}/index.js (97%) rename {ui => examples/preact}/ops.js (94%) rename {ui => examples/preact}/preview.js (100%) rename {ui => examples/preact}/serve.json (100%) rename {ui => examples/preact}/widget.js (98%) create mode 100644 src/camera.js rename {ui => src}/libapi.mjs.d.ts (100%) delete mode 100644 ui/camera.js diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d2f5c59 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +build/* binary +build/* linguist-generated=true \ No newline at end of file diff --git a/Makefile b/Makefile index c5600e6..ecddebf 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ export LDFLAGS += $(COMMON_FLAGS) ## Main API module -ui/libapi.mjs: api.o $(SYSROOT)/lib/libltdl.la $(SYSROOT)/lib/libgphoto2.la +build/libapi.mjs: api.o $(SYSROOT)/lib/libltdl.la $(SYSROOT)/lib/libgphoto2.la libtool --verbose --mode=link $(LD) $(LDFLAGS) -o $@ $+ \ -fexceptions --bind -s ASYNCIFY -s ALLOW_MEMORY_GROWTH \ -dlpreopen $(SYSROOT)/lib/libgphoto2/2.5.28.1/ptp2.la \ diff --git a/ui/libapi.mjs b/build/libapi.mjs similarity index 100% rename from ui/libapi.mjs rename to build/libapi.mjs diff --git a/ui/libapi.wasm b/build/libapi.wasm similarity index 100% rename from ui/libapi.wasm rename to build/libapi.wasm diff --git a/ui/libapi.worker.js b/build/libapi.worker.js similarity index 100% rename from ui/libapi.worker.js rename to build/libapi.worker.js diff --git a/ui/.prettierrc b/examples/preact/.prettierrc similarity index 100% rename from ui/.prettierrc rename to examples/preact/.prettierrc diff --git a/examples/preact/build b/examples/preact/build new file mode 120000 index 0000000..8fdb6a2 --- /dev/null +++ b/examples/preact/build @@ -0,0 +1 @@ +../../build \ No newline at end of file diff --git a/ui/capture-button.js b/examples/preact/capture-button.js similarity index 100% rename from ui/capture-button.js rename to examples/preact/capture-button.js diff --git a/ui/index-fallback.js b/examples/preact/index-fallback.js similarity index 100% rename from ui/index-fallback.js rename to examples/preact/index-fallback.js diff --git a/ui/index.html b/examples/preact/index.html similarity index 100% rename from ui/index.html rename to examples/preact/index.html diff --git a/ui/index.js b/examples/preact/index.js similarity index 97% rename from ui/index.js rename to examples/preact/index.js index 45821f4..0ff1563 100644 --- a/ui/index.js +++ b/examples/preact/index.js @@ -22,9 +22,9 @@ import { connect, rethrowIfCritical } from './ops.js'; import { Preview } from './preview.js'; import { Widget } from './widget.js'; -/** @typedef {import('../libapi.mjs').Context} Context */ -/** @typedef {import('../libapi.mjs').Config} Config */ -/** @typedef {import('./ops').Connection} Connection */ +/** @typedef {import('./build/libapi.mjs').Context} Context */ +/** @typedef {import('./build/libapi.mjs').Config} Config */ +/** @typedef {import('./ops.js').Connection} Connection */ export const isDebug = new URLSearchParams(location.search).has('debug'); diff --git a/ui/ops.js b/examples/preact/ops.js similarity index 94% rename from ui/ops.js rename to examples/preact/ops.js index e3e18f7..566e264 100644 --- a/ui/ops.js +++ b/examples/preact/ops.js @@ -16,7 +16,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -import initModule from '../libapi.mjs'; +import initModule from './build/libapi.mjs'; /** @typedef {import('../libapi.mjs').Context} Context */ @@ -25,7 +25,8 @@ const ModulePromise = initModule(); export function rethrowIfCritical(err) { // If it's precisely Error, it's a custom error; anything else - SyntaxError, // WebAssembly.RuntimeError, TypeError, etc. - is treated as critical here. - if (err.constructor !== Error) { + if (err.constructor !== Error) + { throw err; } } @@ -44,7 +45,8 @@ export async function connect() { * @param {(ctx: Context) => Promise} op * @returns {Promise} */ - function schedule(op) { + function schedule(op) + { let res = queue.then(() => op(context)); queue = res.catch(rethrowIfCritical); return res; diff --git a/ui/preview.js b/examples/preact/preview.js similarity index 100% rename from ui/preview.js rename to examples/preact/preview.js diff --git a/ui/serve.json b/examples/preact/serve.json similarity index 100% rename from ui/serve.json rename to examples/preact/serve.json diff --git a/ui/widget.js b/examples/preact/widget.js similarity index 98% rename from ui/widget.js rename to examples/preact/widget.js index 350cbc2..3bd8c26 100644 --- a/ui/widget.js +++ b/examples/preact/widget.js @@ -18,7 +18,7 @@ import { h, Component, createRef } from 'preact'; -/** @typedef {import('../libapi.mjs').Config} Config */ +/** @typedef {import('./build/libapi.mjs').Config} Config */ /** * @param {Config} config diff --git a/package.json b/package.json index ccee349..a318f09 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,8 @@ "name": "web-gphoto2", "version": "0.1.0", "description": "WebAssembly implementation of gphoto2 and libusb to control DSLR cameras over USB on the Web", - "main": "ui/camera.js", - "types": "ui/libapi.mjs.d.ts", + "main": "src/camera.js", + "types": "src/libapi.mjs.d.ts", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, @@ -23,8 +23,8 @@ }, "homepage": "https://github.com/GoogleChromeLabs/web-gphoto2#readme", "files": [ - "ui/libapi.*", - "ui/camera.js" + "src/*", + "build/*" ], "devDependencies": { "@types/emscripten": "^1.39.5", @@ -32,4 +32,4 @@ "@types/stats.js": "^0.17.0", "preact": "^10.5.14" } -} \ No newline at end of file +} diff --git a/src/camera.js b/src/camera.js new file mode 100644 index 0000000..066bf51 --- /dev/null +++ b/src/camera.js @@ -0,0 +1,116 @@ +/* + * Copyright 2021 Google LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** @typedef {import('./libapi.mjs.d.ts').Context} Context */ + +// To avoid errors for users who use SSR or Hybrid rendering (e.g. Nuxt.js), we need to check if we're in the browser. +let initModule; + +async function initializeModule() { + if (typeof window !== "undefined") { + const module = await import("../build/libapi.mjs"); + initModule = module.default; + return initModule; + } else { + console.warn("web-gphoto2 is only available in the browser"); + return null; + } +} + +export function rethrowIfCritical(err) { + // If it's precisely Error, it's a custom error; anything else - SyntaxError, + // WebAssembly.RuntimeError, TypeError, etc. - is treated as critical here. + if (err.constructor !== Error) { + throw err; + } +} + +class Camera { + constructor() { + /** @type {Promise} */ + this.queue = Promise.resolve(); + this.Module = null; + this.context = null; + this.ModulePromise = null; + } + + async connect() { + if (!this.ModulePromise) { + this.ModulePromise = initializeModule().then((initModule) => { + if (initModule) { + return initModule(); + } else { + return null; + } + }); + } + this.Module = await this.ModulePromise; + this.context = await new this.Module.Context(); + } + + /** Schedules an exclusive async operation on the global context. + * @template T + * @param {(ctx: Context) => Promise} op + * @returns {Promise} + */ + async schedule(op) { + let res = this.queue.then(() => op(this.context)); + this.queue = res.catch(rethrowIfCritical); + return res; + } + + async disconnect() { + if (!this.context.isDeleted()) { + this.context.delete(); + } + } + + async getConfig() { + return this.schedule((context) => context.configToJS()); + } + + async getSupportedOps() { + if (this.context) { + return await this.context.supportedOps(); + } + throw new Error("You need to connect to the camera first"); + } + + async setConfigValue(name, value) { + const uiTimeout = new Promise((resolve) => setTimeout(resolve, 800)); + const setResult = this.schedule((context) => + context.setConfigValue(name, value) + ); + // wait for both the config set operation and the timeout to complete + return Promise.all([setResult, uiTimeout]); + } + + async capturePreviewAsBlob() { + return this.schedule((context) => context.capturePreviewAsBlob()); + } + + async captureImageAsFile() { + return this.schedule((context) => context.captureImageAsFile()); + } + + async consumeEvents() { + return this.schedule((context) => context.consumeEvents()); + } +} + +export default Camera; diff --git a/ui/libapi.mjs.d.ts b/src/libapi.mjs.d.ts similarity index 100% rename from ui/libapi.mjs.d.ts rename to src/libapi.mjs.d.ts diff --git a/ui/camera.js b/ui/camera.js deleted file mode 100644 index 8d315b5..0000000 --- a/ui/camera.js +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - - -/** @typedef {import('./libapi.mjs').Context} Context */ - -// To avoid errors for users who use SSR or Hybrid rendering (e.g. Nuxt.js), we need to check if we're in the browser. -let initModule; - -async function initializeModule() { - if (typeof window !== 'undefined') { - const module = await import('./libapi.mjs'); - initModule = module.default; - return initModule; - } else { - console.warn("web-gphoto2 is only available in the browser"); - return null; - } -} - -export function rethrowIfCritical(err) { - // If it's precisely Error, it's a custom error; anything else - SyntaxError, - // WebAssembly.RuntimeError, TypeError, etc. - is treated as critical here. - if (err.constructor !== Error) { - throw err; - } -} - -class Camera { - constructor() { - /** @type {Promise} */ - this.queue = Promise.resolve(); - this.Module = null; - this.context = null; - this.ModulePromise = null; - } - - async connect() { - if (!this.ModulePromise) { - this.ModulePromise = initializeModule().then(initModule => { - if (initModule) { - return initModule(); - } else { - return null; - } - }); - } - this.Module = await this.ModulePromise; - this.context = await new this.Module.Context(); - } - - /** Schedules an exclusive async operation on the global context. - * @template T - * @param {(ctx: Context) => Promise} op - * @returns {Promise} - */ - async schedule(op) { - let res = this.queue.then(() => op(this.context)); - this.queue = res.catch(rethrowIfCritical); - return res; - } - - async disconnect() { - if (!this.context.isDeleted()) { - this.context.delete(); - } - } - - async getConfig() { - return this.schedule((context) => context.configToJS()); - } - - async getSupportedOps() { - if (this.context) { - return await this.context.supportedOps(); - } - throw new Error("You need to connect to the camera first"); - } - - async setConfigValue(name, value) { - const uiTimeout = new Promise((resolve) => setTimeout(resolve, 800)); - const setResult = this.schedule((context) => context.setConfigValue(name, value)); - // wait for both the config set operation and the timeout to complete - return Promise.all([setResult, uiTimeout]); - } - - - async capturePreviewAsBlob() { - return this.schedule((context) => context.capturePreviewAsBlob()); - } - - async captureImageAsFile() { - return this.schedule((context) => context.captureImageAsFile()); - } - - async consumeEvents() { - return this.schedule((context) => context.consumeEvents()); - } -} - -export default Camera;