@photobot/gphoto2-camera
Version:
This is a small wrapper around libgphoto2 which allows access to some of the methods in the library from NodeJS. It does not provide access to all of the methods in the library, just enough for our purposes.
1,135 lines (1,126 loc) • 40.9 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
CameraEventType: () => CameraEventType,
DEFAULT_LIBC_PATHS: () => DEFAULT_LIBC_PATHS,
DEFAULT_LIBGPHOTO2_PATHS: () => DEFAULT_LIBGPHOTO2_PATHS,
LoggingLevel: () => LoggingLevel,
WidgetType: () => WidgetType,
load: () => load,
loadInternal: () => loadInternal
});
module.exports = __toCommonJS(index_exports);
var import_koffi3 = __toESM(require("koffi"));
var import_fs = __toESM(require("fs"));
// src/ffi/index.ts
var import_koffi2 = __toESM(require("koffi"));
// src/ffi/definitions.ts
var import_koffi = __toESM(require("koffi"));
var setupDefinitions = () => {
const CameraList = import_koffi.default.opaque("CameraList");
const Camera = import_koffi.default.opaque("Camera");
const CameraAbilitiesList = import_koffi.default.opaque("CameraAbilitiesList");
const CameraAbilities = import_koffi.default.struct("CameraAbilities", {
model: import_koffi.default.array("char", 128),
status: "unsigned int",
port: "unsigned int",
speed: import_koffi.default.array("int", 64),
operations: "unsigned int",
file_operations: "unsigned int",
folder_operations: "unsigned int",
usb_vendor: "int",
usb_product: "int",
usb_class: "int",
usb_subclass: "int",
usb_protocol: "int",
library: import_koffi.default.array("char", 1024),
id: import_koffi.default.array("char", 1024),
device_type: "unsigned int",
reserved2: "int",
reserved3: "int",
reserved4: "int",
reserved5: "int",
reserved6: "int",
reserved7: "int",
reserved8: "int"
});
const GPPortInfoList = import_koffi.default.opaque("GPPortInfoList");
const GPPortInfo = import_koffi.default.struct("GPPortInfo", {
type: "unsigned int",
name: "char",
path: "char",
library_filename: "char"
});
const CameraText = import_koffi.default.struct("CameraText", {
text: import_koffi.default.array("char", 32 * 1024)
});
const CameraEventType2 = import_koffi.default.alias("CameraEventType", "unsigned int");
const CameraFileType = import_koffi.default.alias("CameraFileType", "unsigned int");
const CameraFilePath2 = import_koffi.default.struct("CameraFilePath", {
name: import_koffi.default.array("char", 128),
folder: import_koffi.default.array("char", 1024)
});
const CameraFile = import_koffi.default.opaque("CameraFile");
const CameraWidget = import_koffi.default.opaque("CameraWidget");
const CameraWidgetType = import_koffi.default.alias("CameraWidgetType", "unsigned int");
const GPContextIdleFunc = import_koffi.default.pointer(
"GPContextIdleFunc",
import_koffi.default.opaque(),
1
);
const GPContextProgressStartFunc = import_koffi.default.pointer(
"GPContextProgressStartFunc",
import_koffi.default.opaque(),
1
);
const GPContextProgressUpdateFunc = import_koffi.default.pointer(
"GPContextProgressUpdateFunc",
import_koffi.default.opaque(),
1
);
const GPContextProgressStopFunc = import_koffi.default.pointer(
"GPContextProgressStopFunc",
import_koffi.default.opaque(),
1
);
const GPContextErrorFunc = import_koffi.default.pointer(
"GPContextErrorFunc",
import_koffi.default.opaque(),
1
);
const GPContextQuestionFunc = import_koffi.default.pointer(
"GPContextQuestionFunc",
import_koffi.default.opaque(),
1
);
const GPContextCancelFunc = import_koffi.default.pointer(
"GPContextCancelFunc",
import_koffi.default.opaque(),
1
);
const GPContextStatusFunc = import_koffi.default.pointer(
"GPContextStatusFunc",
import_koffi.default.opaque(),
1
);
const GPContextMessageFunc = import_koffi.default.pointer(
"GPContextMessageFunc",
import_koffi.default.opaque(),
1
);
const GPContext = import_koffi.default.struct("GPContext", {
idle_func: GPContextIdleFunc,
idle_func_data: "void *",
progress_start_func: GPContextProgressStartFunc,
progress_update_func: GPContextProgressUpdateFunc,
progress_stop_func: GPContextProgressStopFunc,
progress_func_data: "void *",
error_func: GPContextErrorFunc,
error_func_data: "void *",
question_func: GPContextQuestionFunc,
question_func_data: "void *",
cancel_func: GPContextCancelFunc,
cancel_func_data: "void *",
status_func: GPContextStatusFunc,
status_func_data: "void *",
message_func: GPContextMessageFunc,
message_func_data: "void *",
ref_count: "unsigned int"
});
const GPLogFunc = import_koffi.default.proto(
"void GPLogFunc(int level, const char* domain, const char* str, void* data)"
);
return {
CameraList,
Camera,
CameraAbilitiesList,
CameraAbilities,
CameraText,
GPPortInfoList,
GPPortInfo,
CameraEventType: CameraEventType2,
CameraFileType,
CameraFilePath: CameraFilePath2,
CameraFile,
CameraWidget,
CameraWidgetType,
GPContextIdleFunc,
GPContextProgressStartFunc,
GPContextProgressUpdateFunc,
GPContextProgressStopFunc,
GPContextErrorFunc,
GPContextQuestionFunc,
GPContextCancelFunc,
GPContextStatusFunc,
GPContextMessageFunc,
GPContext,
GPLogFunc
};
};
// src/ffi/functions.ts
var import_util = require("util");
var setupFunctions = (libgphoto2, libc) => {
const createAsyncFunc = (lib, def) => {
const func = lib.func(def);
return (...args) => (0, import_util.promisify)(func.async)(...args);
};
const createErrorCheckingAsyncFunc = (lib, def) => {
const func = createAsyncFunc(lib, def);
return (...args) => func(...args).then((ret) => {
if (typeof ret !== "number") {
throw new Error("Unknown return type");
} else if (ret < 0) {
throw new Error(`Error ${ret}`);
} else {
return ret;
}
});
};
const gp_abilities_list_new = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_abilities_list_new(_Out_ CameraAbilitiesList** list)"
);
const gp_abilities_list_load = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_abilities_list_load(_Out_ CameraAbilitiesList* list, GPContext* context)"
);
const gp_abilities_list_lookup_model = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_abilities_list_lookup_model(CameraAbilitiesList* list, char* model)"
);
const gp_abilities_list_get_abilities = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_abilities_list_get_abilities(CameraAbilitiesList* list, int index, _Out_ CameraAbilities* abilities)"
);
const gp_abilities_list_free = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_abilities_list_free(CameraAbilitiesList* list)"
);
const gp_port_info_list_new = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_port_info_list_new(_Out_ GPPortInfoList** list)"
);
const gp_port_info_list_load = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_port_info_list_load(GPPortInfoList* list)"
);
const gp_port_info_list_lookup_path = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_port_info_list_lookup_path(GPPortInfoList* list, char* path)"
);
const gp_port_info_list_get_info = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_port_info_list_get_info(GPPortInfoList* list, int n, _Out_ GPPortInfo* info)"
);
const gp_port_info_list_free = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_port_info_list_free(GPPortInfoList* list)"
);
const gp_context_new = createAsyncFunc(
libgphoto2,
"GPContext* gp_context_new(void)"
);
const gp_list_new = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_list_new(_Out_ CameraList** list)"
);
const gp_list_unref = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_list_unref(CameraList* list)"
);
const gp_list_count = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_list_count(CameraList* list)"
);
const gp_list_get_name = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_list_get_name(CameraList* list, int index, _Out_ char** name)"
);
const gp_list_get_value = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_list_get_value(CameraList* list, int index, _Out_ char** value)"
);
const gp_camera_autodetect = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_camera_autodetect(_Out_ CameraList* list, GPContext* context)"
);
const gp_camera_new = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_camera_new(_Out_ Camera** camera)"
);
const gp_camera_set_abilities = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_camera_set_abilities(Camera* camera, CameraAbilities abilities)"
);
const gp_camera_set_port_info = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_camera_set_port_info(Camera* camera, GPPortInfo abilities)"
);
const gp_camera_init = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_camera_init(Camera* camera, GPContext* context)"
);
const gp_camera_get_summary = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_camera_get_summary(Camera* camera, _Out_ CameraText* summary, GPContext* context)"
);
const gp_camera_trigger_capture = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_camera_trigger_capture(Camera* camera, GPContext* context)"
);
const gp_camera_capture_preview = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_camera_capture_preview(Camera* camera, _Out_ CameraFile* file, GPContext* context)"
);
const gp_camera_wait_for_event = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_camera_wait_for_event(Camera* camera, int timeout, _Out_ CameraEventType* eventtype, _Out_ void** eventdata, GPContext* context)"
);
const gp_camera_file_get = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_camera_file_get(Camera* camera, const char* folder, const char* file, CameraFileType type, _Out_ CameraFile* camera_file, GPContext* context)"
);
const gp_camera_file_delete = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_camera_file_delete(Camera* camera, const char* folder, const char* file, GPContext* context)"
);
const gp_camera_get_config = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_camera_get_config(Camera* camera, _Out_ CameraWidget** window, GPContext* context)"
);
const gp_camera_set_config = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_camera_set_config(Camera* camera, CameraWidget* window, GPContext* context)"
);
const gp_camera_exit = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_camera_exit(Camera* camera, GPContext* context)"
);
const gp_camera_unref = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_camera_unref(Camera* camera)"
);
const gp_widget_count_children = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_widget_count_children(CameraWidget* widget)"
);
const gp_widget_get_child = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_widget_get_child(CameraWidget *widget, int child_number, _Out_ CameraWidget **child)"
);
const gp_widget_get_child_by_name = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_widget_get_child_by_name(CameraWidget* widget, const char* name, _Out_ CameraWidget** child)"
);
const gp_widget_get_child_by_label = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_widget_get_child_by_label(CameraWidget* widget, const char* label, _Out_ CameraWidget** child)"
);
const gp_widget_get_name = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_widget_get_name(CameraWidget* widget, _Out_ const char** name)"
);
const gp_widget_get_label = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_widget_get_label(CameraWidget* widget, _Out_ const char** label)"
);
const gp_widget_get_readonly = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_widget_get_readonly(CameraWidget* widget, _Out_ int* readonly)"
);
const gp_widget_get_type = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_widget_get_type(CameraWidget* widget, _Out_ CameraWidgetType* type)"
);
const gp_widget_get_range = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_widget_get_range(CameraWidget* range, _Out_ float* min, _Out_ float* max, _Out_ float* increment)"
);
const gp_widget_count_choices = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_widget_count_choices(CameraWidget* widget)"
);
const gp_widget_get_choice = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_widget_get_choice(CameraWidget* widget, int choice_number, _Out_ const char** choice)"
);
const gp_widget_get_value_string = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_widget_get_value(CameraWidget* widget, _Out_ const char** value)"
);
const gp_widget_get_value_float = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_widget_get_value(CameraWidget* widget, _Out_ float* value)"
);
const gp_widget_set_value = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_widget_set_value(CameraWidget* widget, const void* value)"
);
const gp_widget_unref = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_widget_unref(CameraWidget* widget)"
);
const gp_file_new = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_file_new(_Out_ CameraFile** file)"
);
const gp_file_new_from_fd = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_file_new_from_fd(_Out_ CameraFile** file, int fd)"
);
const gp_file_get_data_and_size = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_file_get_data_and_size(CameraFile* file, _Out_ const void** data, _Out_ unsigned int* size)"
);
const gp_file_get_mime_type = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_file_get_mime_type(CameraFile* file, _Out_ const char** mime_type)"
);
const gp_file_free = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_file_free(CameraFile* file)"
);
const gp_log_add_func = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_log_add_func(int level, GPLogFunc* func, void* data)"
);
const gp_log_remove_func = createErrorCheckingAsyncFunc(
libgphoto2,
"int gp_log_remove_func(int id)"
);
const open = createAsyncFunc(
libc,
"int open(const char *pathname, int flags, uint32 mode)"
);
const fchmod = createAsyncFunc(libc, "int fchmod(int fd, uint32 mode)");
return {
gp_abilities_list_new,
gp_abilities_list_load,
gp_abilities_list_lookup_model,
gp_abilities_list_get_abilities,
gp_abilities_list_free,
gp_port_info_list_new,
gp_port_info_list_load,
gp_port_info_list_lookup_path,
gp_port_info_list_get_info,
gp_port_info_list_free,
gp_context_new,
gp_list_new,
gp_list_unref,
gp_list_count,
gp_list_get_name,
gp_list_get_value,
gp_camera_autodetect,
gp_camera_new,
gp_camera_set_abilities,
gp_camera_set_port_info,
gp_camera_init,
gp_camera_get_summary,
gp_camera_trigger_capture,
gp_camera_capture_preview,
gp_camera_wait_for_event,
gp_camera_file_get,
gp_camera_file_delete,
gp_camera_get_config,
gp_camera_set_config,
gp_camera_exit,
gp_camera_unref,
gp_widget_count_children,
gp_widget_get_child,
gp_widget_get_child_by_name,
gp_widget_get_child_by_label,
gp_widget_get_name,
gp_widget_get_label,
gp_widget_get_readonly,
gp_widget_get_type,
gp_widget_get_range,
gp_widget_count_choices,
gp_widget_get_choice,
gp_widget_get_value_string,
gp_widget_get_value_float,
gp_widget_set_value,
gp_widget_unref,
gp_file_new,
gp_file_new_from_fd,
gp_file_get_data_and_size,
gp_file_get_mime_type,
gp_file_free,
gp_log_add_func,
gp_log_remove_func,
open,
fchmod
};
};
// src/ffi/index.ts
var ffi;
var getFfi = (options) => {
if (ffi) {
return ffi;
}
const { libgphoto2Paths, libcPaths } = options;
let libgphoto2;
for (const libPath of libgphoto2Paths) {
try {
libgphoto2 = import_koffi2.default.load(libPath);
console.debug(`Loaded library from ${libPath}`);
break;
} catch (_e) {
console.debug(
`Unable to load libgphoto2 from ${libPath}. Trying next...`
);
}
}
if (!libgphoto2) {
throw new Error("Unable to load libgphoto2. Make sure it is installed");
}
let libc;
for (const libPath of libcPaths) {
try {
libc = import_koffi2.default.load(libPath);
console.debug(`Loaded library from ${libPath}`);
break;
} catch (_e) {
console.debug(`Unable to load libc from ${libPath}. Trying next...`);
}
}
if (!libc) {
throw new Error("Unable to load libc. Make sure it is installed");
}
const definitions = setupDefinitions();
const functions = setupFunctions(libgphoto2, libc);
ffi = __spreadProps(__spreadValues(__spreadValues({}, definitions), functions), { koffi: import_koffi2.default });
return ffi;
};
var makeArrayPointer = (values = []) => [...values, null];
// src/types.ts
var WidgetType = /* @__PURE__ */ ((WidgetType2) => {
WidgetType2[WidgetType2["Window"] = 0] = "Window";
WidgetType2[WidgetType2["Section"] = 1] = "Section";
WidgetType2[WidgetType2["Text"] = 2] = "Text";
WidgetType2[WidgetType2["Range"] = 3] = "Range";
WidgetType2[WidgetType2["Toggle"] = 4] = "Toggle";
WidgetType2[WidgetType2["Radio"] = 5] = "Radio";
WidgetType2[WidgetType2["Menu"] = 6] = "Menu";
WidgetType2[WidgetType2["Button"] = 7] = "Button";
WidgetType2[WidgetType2["Date"] = 8] = "Date";
return WidgetType2;
})(WidgetType || {});
var CameraEventType = /* @__PURE__ */ ((CameraEventType2) => {
CameraEventType2[CameraEventType2["Unknown"] = 0] = "Unknown";
CameraEventType2[CameraEventType2["Timeout"] = 1] = "Timeout";
CameraEventType2[CameraEventType2["FileAdded"] = 2] = "FileAdded";
CameraEventType2[CameraEventType2["FolderAdded"] = 3] = "FolderAdded";
CameraEventType2[CameraEventType2["CaptureComplete"] = 4] = "CaptureComplete";
CameraEventType2[CameraEventType2["FileChanged"] = 5] = "FileChanged";
return CameraEventType2;
})(CameraEventType || {});
var LoggingLevel = /* @__PURE__ */ ((LoggingLevel3) => {
LoggingLevel3[LoggingLevel3["Error"] = 0] = "Error";
LoggingLevel3[LoggingLevel3["Verbose"] = 1] = "Verbose";
LoggingLevel3[LoggingLevel3["Debug"] = 2] = "Debug";
LoggingLevel3[LoggingLevel3["Data"] = 3] = "Data";
return LoggingLevel3;
})(LoggingLevel || {});
// src/constants.ts
var DEFAULT_LIBGPHOTO2_PATHS = [
// Linux
"/usr/lib/aarch64-linux-gnu/libgphoto2.so.6",
"libgphoto2.so.6",
// macOS
"libgphoto2.dylib",
"/opt/homebrew/lib/libgphoto2.dylib",
"/usr/local/lib/libgphoto2.dylib"
];
var DEFAULT_LIBC_PATHS = [
// Linux
"libc.so.6",
// macOS
"libc.dylib"
];
// src/index.ts
var GP_OK = 0;
var GP_ERROR_UNKNOWN_PORT = -5;
var GP_FILE_TYPE_NORMAL = 1;
var loadInternal = async (options) => {
const ffi2 = getFfi({
libgphoto2Paths: options.libgphoto2Paths || DEFAULT_LIBGPHOTO2_PATHS,
libcPaths: options.libcPaths || DEFAULT_LIBC_PATHS
});
const context = await ffi2.gp_context_new();
const openCameraRefs = /* @__PURE__ */ new Map();
const _getOpenCamera = (cameraInfo) => {
const camera = openCameraRefs.get(cameraInfo.port);
if (!camera) {
throw new Error("Camera needs to be opened first");
}
return camera;
};
const _forEachCameraConfig = async (camera, handler) => {
const rootConfigWidgetPointer = makeArrayPointer();
await ffi2.gp_camera_get_config(camera, rootConfigWidgetPointer, context);
const rootConfigWidget = rootConfigWidgetPointer[0];
try {
const getConfig = async (widget, prefix = "") => {
const widgetNamePointer = makeArrayPointer();
await ffi2.gp_widget_get_name(widget, widgetNamePointer);
const widgetName = widgetNamePointer[0];
const typePointer = makeArrayPointer();
await ffi2.gp_widget_get_type(widget, typePointer);
const widgetType = typePointer[0];
await handler({ prefix, widgetName, widgetType, widget });
const childCount = await ffi2.gp_widget_count_children(widget);
for (let i = 0; i < childCount; i += 1) {
const childWidgetPointer = makeArrayPointer();
await ffi2.gp_widget_get_child(widget, i, childWidgetPointer);
const childWidget = childWidgetPointer[0];
await getConfig(childWidget, prefix + widgetName + "/");
}
};
await getConfig(rootConfigWidget);
} finally {
await ffi2.gp_widget_unref(rootConfigWidget);
}
};
const listAsync = async () => {
const listPointer = makeArrayPointer();
await ffi2.gp_list_new(listPointer);
const list = listPointer[0];
await ffi2.gp_camera_autodetect(list, context);
const count = await ffi2.gp_list_count(list);
const cameras = [];
for (let cameraIndex = 0; cameraIndex < count; cameraIndex += 1) {
const namePointer = makeArrayPointer();
await ffi2.gp_list_get_name(list, cameraIndex, namePointer);
const name = namePointer[0];
const portPointer = makeArrayPointer();
await ffi2.gp_list_get_value(list, cameraIndex, portPointer);
const port = portPointer[0];
cameras.push({ name, port });
}
await ffi2.gp_list_unref(list);
return cameras;
};
const openAsync = async (cameraInfo) => {
if (openCameraRefs.has(cameraInfo.port)) {
return;
}
const cameraPointer = makeArrayPointer();
await ffi2.gp_camera_new(cameraPointer);
const camera = cameraPointer[0];
const abilitiesListPointer = makeArrayPointer();
await ffi2.gp_abilities_list_new(abilitiesListPointer);
const abilitiesList = abilitiesListPointer[0];
await ffi2.gp_abilities_list_load(abilitiesList, context);
const modelIndex = await ffi2.gp_abilities_list_lookup_model(
abilitiesList,
cameraInfo.name
);
const cameraAbilitiesPointer = makeArrayPointer();
await ffi2.gp_abilities_list_get_abilities(
abilitiesList,
modelIndex,
cameraAbilitiesPointer
);
const cameraAbilities = cameraAbilitiesPointer[0];
await ffi2.gp_camera_set_abilities(camera, cameraAbilities);
await ffi2.gp_abilities_list_free(abilitiesList);
const portInfoListPointer = makeArrayPointer();
await ffi2.gp_port_info_list_new(portInfoListPointer);
const portInfoList = portInfoListPointer[0];
await ffi2.gp_port_info_list_load(portInfoList);
const portIndex = await ffi2.gp_port_info_list_lookup_path(
portInfoList,
cameraInfo.port
);
if (portIndex === GP_ERROR_UNKNOWN_PORT) {
throw new Error("Unknown port");
}
const portInfoPointer = makeArrayPointer();
await ffi2.gp_port_info_list_get_info(
portInfoList,
portIndex,
portInfoPointer
);
const portInfo = portInfoPointer[0];
await ffi2.gp_camera_set_port_info(camera, portInfo);
await ffi2.gp_port_info_list_free(portInfoList);
await ffi2.gp_camera_init(camera, context);
openCameraRefs.set(cameraInfo.port, camera);
};
const closeAsync = async (cameraInfo) => {
const camera = openCameraRefs.get(cameraInfo.port);
if (!camera) {
return false;
}
try {
await ffi2.gp_camera_exit(camera, context);
} catch (e) {
console.warn("Error while exiting camera. Ignoring", e);
}
try {
await ffi2.gp_camera_unref(camera);
} catch (e) {
console.warn("Error while unrefing camera. Ignoring", e);
}
openCameraRefs.delete(cameraInfo.port);
return true;
};
const summaryAsync = async (cameraInfo) => {
const camera = _getOpenCamera(cameraInfo);
const cameraTextPointer = makeArrayPointer();
await ffi2.gp_camera_get_summary(camera, cameraTextPointer, context);
const cameraText = cameraTextPointer[0];
return cameraText.text;
};
const triggerCaptureAsync = async (cameraInfo) => {
const camera = _getOpenCamera(cameraInfo);
await ffi2.gp_camera_trigger_capture(camera, context);
};
const triggerCapturePreviewAsync = async (cameraInfo) => {
const camera = _getOpenCamera(cameraInfo);
const filePointer = makeArrayPointer();
await ffi2.gp_file_new(filePointer);
const file = filePointer[0];
await ffi2.gp_camera_capture_preview(camera, file, context);
const dataPointer = makeArrayPointer();
const sizePointer = makeArrayPointer();
await ffi2.gp_file_get_data_and_size(file, dataPointer, sizePointer);
const size = sizePointer[0];
const data = import_koffi3.default.decode(
dataPointer[0],
import_koffi3.default.array("char", size, "Typed")
);
const mimeTypePointer = makeArrayPointer();
await ffi2.gp_file_get_mime_type(file, mimeTypePointer);
const mimeType = mimeTypePointer[0];
await ffi2.gp_file_free(file);
return { data, size, mimeType };
};
const getConfigAsync = async (cameraInfo, options2 = {}) => {
const camera = _getOpenCamera(cameraInfo);
const config = {};
await _forEachCameraConfig(
camera,
async ({ prefix, widgetName, widget, widgetType }) => {
const configKey = prefix + widgetName;
let shouldInclude = options2.filter ? options2.filter(widgetName, widgetType) : true;
if (shouldInclude && options2.ignoreReadOnly) {
const readOnlyPointer = makeArrayPointer();
await ffi2.gp_widget_get_readonly(widget, readOnlyPointer);
shouldInclude = readOnlyPointer[0] !== 1;
}
if (shouldInclude) {
try {
switch (widgetType) {
// Get the value as a string
case 6 /* Menu */:
case 5 /* Radio */:
case 2 /* Text */: {
const valuePointer = makeArrayPointer();
await ffi2.gp_widget_get_value_string(widget, valuePointer);
config[configKey] = valuePointer[0];
break;
}
// Get the value as a number
case 3 /* Range */: {
const valuePointer = makeArrayPointer();
await ffi2.gp_widget_get_value_float(widget, valuePointer);
config[configKey] = valuePointer[0];
break;
}
case 4 /* Toggle */: {
const valuePointer = makeArrayPointer();
await ffi2.gp_widget_get_value_float(widget, valuePointer);
config[configKey] = !!valuePointer[0];
break;
}
}
} catch (e) {
console.warn(`Unable to get value for ${widgetName}`, e);
}
}
}
);
return config;
};
const getConfigWidgetsAsync = async (cameraInfo, options2 = {}) => {
const camera = _getOpenCamera(cameraInfo);
const config = {};
await _forEachCameraConfig(
camera,
async ({ prefix, widgetName, widget, widgetType }) => {
const configKey = prefix + widgetName;
let shouldInclude = options2.filter ? options2.filter(widgetName, widgetType) : true;
if (shouldInclude && options2.ignoreReadOnly) {
const readOnlyPointer = makeArrayPointer();
await ffi2.gp_widget_get_readonly(widget, readOnlyPointer);
shouldInclude = readOnlyPointer[0] !== 1;
}
if (shouldInclude) {
const labelPointer = makeArrayPointer();
await ffi2.gp_widget_get_label(widget, labelPointer);
const widgetLabel = labelPointer[0];
try {
switch (widgetType) {
// Get the value as a string
case 2 /* Text */: {
const valuePointer = makeArrayPointer();
await ffi2.gp_widget_get_value_string(widget, valuePointer);
config[configKey] = {
type: widgetType,
label: widgetLabel,
value: valuePointer[0]
};
break;
}
// Get the value as a number
case 3 /* Range */: {
const valuePointer = makeArrayPointer();
await ffi2.gp_widget_get_value_float(widget, valuePointer);
const minPointer = makeArrayPointer();
const maxPointer = makeArrayPointer();
const incrementPointer = makeArrayPointer();
await ffi2.gp_widget_get_range(
widget,
minPointer,
maxPointer,
incrementPointer
);
config[configKey] = {
type: widgetType,
label: widgetLabel,
value: valuePointer[0],
min: minPointer[0],
max: maxPointer[0],
increment: incrementPointer[0]
};
break;
}
// Get the value as a float and then convert to a boolean
case 4 /* Toggle */: {
const valuePointer = makeArrayPointer();
await ffi2.gp_widget_get_value_float(widget, valuePointer);
config[configKey] = {
type: widgetType,
label: widgetLabel,
value: !!valuePointer[0]
};
break;
}
case 6 /* Menu */:
case 5 /* Radio */: {
const valuePointer = makeArrayPointer();
await ffi2.gp_widget_get_value_string(widget, valuePointer);
const choiceCount = await ffi2.gp_widget_count_choices(widget);
const choices = [];
for (let i = 0; i < choiceCount; i++) {
const choicePointer = makeArrayPointer();
await ffi2.gp_widget_get_choice(widget, i, choicePointer);
choices.push(choicePointer[0]);
}
config[configKey] = {
type: widgetType,
label: widgetLabel,
value: valuePointer[0],
choices
};
break;
}
}
} catch (e) {
console.warn(`Unable to get value for ${widgetName}`, e);
}
}
}
);
return config;
};
const setConfigAsync = async (cameraInfo, newConfig, options2 = {}) => {
const throwOrWarn = (message) => {
if (options2.ignoreErrors) {
console.warn(message);
} else {
throw new Error(message);
}
};
const camera = _getOpenCamera(cameraInfo);
const rootConfigPointer = makeArrayPointer();
await ffi2.gp_camera_get_config(camera, rootConfigPointer, context);
const rootConfig = rootConfigPointer[0];
try {
const entires = Object.entries(newConfig);
entriesLoop: for (const [name, value] of entires) {
const nameParts = name.split("/");
const lastNamePart = nameParts.length > 0 ? nameParts[nameParts.length - 1] : name;
const widgetPointer = makeArrayPointer();
let getConfigRet = await ffi2.gp_widget_get_child_by_name(
rootConfig,
lastNamePart,
widgetPointer
);
if (getConfigRet < GP_OK) {
getConfigRet = await ffi2.gp_widget_get_child_by_label(
rootConfig,
lastNamePart,
widgetPointer
);
}
if (getConfigRet < GP_OK) {
throwOrWarn(`Unable to get config with name ${name}`);
continue entriesLoop;
}
const widget = widgetPointer[0];
const readOnlyPointer = makeArrayPointer();
await ffi2.gp_widget_get_readonly(widget, readOnlyPointer);
const readOnly = readOnlyPointer[0];
if (readOnly === 1) {
throwOrWarn(`Unable to change the value of readonly config ${name}`);
continue entriesLoop;
}
const typePointer = makeArrayPointer();
await ffi2.gp_widget_get_type(widget, typePointer);
const type = typePointer[0];
switch (type) {
// For text types: just set the value
case 2 /* Text */:
if (typeof value !== "string") {
throwOrWarn(
`Must pass a string when setting ${name} with a text type`
);
continue entriesLoop;
}
await ffi2.gp_widget_set_value(widget, value);
break;
// For range types: parse as a float, check the range is correct, and then set it
case 3 /* Range */: {
const floatValue = typeof value === "string" ? parseFloat(value) : typeof value === "boolean" ? value === true ? 1 : 0 : value;
if (Number.isNaN(floatValue)) {
throwOrWarn(
`Must pass a float string when setting ${name} with a range type. Got ${value}`
);
continue entriesLoop;
}
const maxPointer = makeArrayPointer();
const minPointer = makeArrayPointer();
const incrementPointer = makeArrayPointer();
await ffi2.gp_widget_get_range(
widget,
minPointer,
maxPointer,
incrementPointer
);
const max = maxPointer[0];
const min = minPointer[0];
if (floatValue > max || floatValue < min) {
throwOrWarn(
`Must pass a float string between ${min} and ${max} when setting ${name} with a range type. Got ${value}`
);
continue entriesLoop;
}
await ffi2.gp_widget_set_value(
widget,
import_koffi3.default.as([floatValue], "float *")
);
break;
}
// For toggle types: Check if we got a "true" string and set the number value based on that
case 4 /* Toggle */: {
const intValue = value === true || value === "true" || value === 1 ? 1 : 0;
await ffi2.gp_widget_set_value(
widget,
import_koffi3.default.as([intValue], "int *")
);
break;
}
// For menu or radio types: go through each choice to ensure the value is one of the options. If so, set it
case 6 /* Menu */:
case 5 /* Radio */: {
if (typeof value !== "string") {
throwOrWarn(
`Must pass a string when setting ${name} with a menu or radio type`
);
continue entriesLoop;
}
const choiceCount = await ffi2.gp_widget_count_choices(widget);
let wasSet = false;
for (let choiceIndex = 0; choiceIndex < choiceCount; choiceIndex += 1) {
const choicePointer = makeArrayPointer();
await ffi2.gp_widget_get_choice(
widget,
choiceIndex,
choicePointer
);
const choice = choicePointer[0];
if (choice === value) {
await ffi2.gp_widget_set_value(widget, value);
wasSet = true;
break;
}
}
if (!wasSet) {
throwOrWarn(
`Unable to set config ${name} to invalid choice ${value}`
);
continue entriesLoop;
}
break;
}
default:
throwOrWarn(`Unable to change config ${name} with type ${type}`);
continue entriesLoop;
}
}
await ffi2.gp_camera_set_config(camera, rootConfig, context);
} finally {
await ffi2.gp_widget_unref(rootConfig);
}
};
const waitForEventAsync = async (cameraInfo, timeoutMilliseconds) => {
const camera = _getOpenCamera(cameraInfo);
const eventTypePointer = makeArrayPointer();
const eventDataPointer = makeArrayPointer();
await ffi2.gp_camera_wait_for_event(
camera,
timeoutMilliseconds,
eventTypePointer,
eventDataPointer,
context
);
const eventType = eventTypePointer[0];
const eventData = eventDataPointer[0];
switch (eventType) {
// For event types which include CameraFilePath's as data, we need to decode this and include it in the event
case 2 /* FileAdded */:
case 5 /* FileChanged */:
case 3 /* FolderAdded */: {
const cameraFilePath = import_koffi3.default.decode(
eventData,
"CameraFilePath"
);
return { type: eventType, path: cameraFilePath };
}
// Otherwise, we just return the event number
default:
return { type: eventType };
}
};
const getFileAsync = async (cameraInfo, cameraFilePath, targetFilePath) => {
const camera = _getOpenCamera(cameraInfo);
const fileMode = import_fs.default.constants.S_IWUSR | import_fs.default.constants.S_IRUSR | import_fs.default.constants.S_IRGRP | import_fs.default.constants.S_IROTH;
const fd = await ffi2.open(
targetFilePath,
import_fs.default.constants.O_CREAT | import_fs.default.constants.O_TRUNC | import_fs.default.constants.O_WRONLY,
fileMode
);
await ffi2.fchmod(fd, fileMode);
const filePointer = makeArrayPointer();
await ffi2.gp_file_new_from_fd(filePointer, fd);
const file = filePointer[0];
await ffi2.gp_camera_file_get(
camera,
cameraFilePath.folder,
cameraFilePath.name,
GP_FILE_TYPE_NORMAL,
file,
context
);
await ffi2.gp_file_free(file);
await ffi2.gp_camera_file_delete(
camera,
cameraFilePath.folder,
cameraFilePath.name,
context
);
};
const addLogFunc = async (level, loggingFunc) => {
const loggingCallback = ffi2.koffi.register(
loggingFunc,
ffi2.koffi.pointer(ffi2.GPLogFunc)
);
const loggingId = await ffi2.gp_log_add_func(
level,
loggingCallback,
void 0
);
return () => {
ffi2.gp_log_remove_func(loggingId);
};
};
return {
ffi: ffi2,
listAsync,
openAsync,
closeAsync,
summaryAsync,
triggerCaptureAsync,
triggerCapturePreviewAsync,
getConfigAsync,
getConfigWidgetsAsync,
setConfigAsync,
waitForEventAsync,
getFileAsync,
addLogFunc
};
};
var loadedLib;
var load = async (options = {}) => {
if (!loadedLib) {
loadedLib = await loadInternal(options);
}
return loadedLib;
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
CameraEventType,
DEFAULT_LIBC_PATHS,
DEFAULT_LIBGPHOTO2_PATHS,
LoggingLevel,
WidgetType,
load,
loadInternal
});