@camstack/addon-pipeline-orchestrator 1.2.264 → 1.2.265
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_stub.js +2 -2
- package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-DSjT-nms.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-DcObJv8j.mjs} +2 -2
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-4LsLRtwM.mjs +26 -0
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BM06SN3k.mjs +26 -0
- package/dist/{hostInit-B8wR4j8o.mjs → hostInit-veHYbLfI.mjs} +2 -2
- package/dist/index.js +234 -43
- package/dist/index.mjs +234 -43
- package/dist/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Cl5U0LCN.mjs +0 -26
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-tzKjeTfv.mjs +0 -26
package/dist/index.js
CHANGED
|
@@ -8036,7 +8036,59 @@ function errMsg$1(err) {
|
|
|
8036
8036
|
return String(err);
|
|
8037
8037
|
}
|
|
8038
8038
|
//#endregion
|
|
8039
|
-
//#region ../types/dist/composition-
|
|
8039
|
+
//#region ../types/dist/composition-Dq42XWp0.mjs
|
|
8040
|
+
/**
|
|
8041
|
+
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
8042
|
+
*
|
|
8043
|
+
* ## Why a download, on every node, including the container
|
|
8044
|
+
*
|
|
8045
|
+
* The version of ffmpeg used to be CODE: baked into the image, changeable only
|
|
8046
|
+
* by rebuilding and rolling it. Now it is DATA — this file states the release,
|
|
8047
|
+
* and a node that boots with a different one downloads it. Changing ffmpeg is a
|
|
8048
|
+
* framework bump, not an image rebuild.
|
|
8049
|
+
*
|
|
8050
|
+
* It also settles the licence question by removing it. FFmpeg's licence is
|
|
8051
|
+
* chosen at compile time: the core is LGPL 2.1+, `--enable-gpl` makes it GPL
|
|
8052
|
+
* (for x264/x265), `--enable-version3` makes that v3, and `--enable-nonfree`
|
|
8053
|
+
* makes the result undistributable. These are GPL v3 builds, and we do not
|
|
8054
|
+
* distribute them: the user's own node fetches the archive from the project
|
|
8055
|
+
* that publishes it, so we convey no GPL work and inherit none of the
|
|
8056
|
+
* obligations that come with conveying one. That is the whole reason the image
|
|
8057
|
+
* no longer installs ffmpeg at all — not a technical preference.
|
|
8058
|
+
*
|
|
8059
|
+
* ## Why jellyfin-ffmpeg
|
|
8060
|
+
*
|
|
8061
|
+
* One vendor covering linux-amd64, linux-arm64, darwin-arm64 and darwin-x64
|
|
8062
|
+
* from one release, with the widest hardware surface of anything maintained:
|
|
8063
|
+
* VAAPI, QSV, NVENC/NVDEC, AMF, Vulkan, OpenCL and DRM on amd64, plus Rockchip
|
|
8064
|
+
* MPP on arm64. It reaches the vendor libraries through dlopen trampolines,
|
|
8065
|
+
* so the binary is not linked against a driver stack it may not find.
|
|
8066
|
+
*
|
|
8067
|
+
* **What it still needs from the host, measured 2026-09-19.** On the Unraid
|
|
8068
|
+
* HOST, which has no libva, `-vaapi_device` aborts the process outright —
|
|
8069
|
+
* `implib-gen: libva-drm.so.2: failed to load library ... Assertion '0 &&
|
|
8070
|
+
* "Assertion in generated code"' failed`. Inside our container, which installs
|
|
8071
|
+
* libva plus the iHD driver, the same command encodes: verified end to end with
|
|
8072
|
+
* `testsrc → format=nv12,hwupload → h264_vaapi`. So the image drops ffmpeg and
|
|
8073
|
+
* KEEPS the Intel media stack; the drivers are the part a binary cannot bring.
|
|
8074
|
+
*
|
|
8075
|
+
* ## Why each artifact carries a checksum and a claim
|
|
8076
|
+
*
|
|
8077
|
+
* The download is now the only source, so `sha256` is verified before anything
|
|
8078
|
+
* is extracted — jellyfin publishes no checksums on its release assets, so
|
|
8079
|
+
* these are ours, taken at the version bump. And `expectedHwaccels` is what the
|
|
8080
|
+
* build is supposed to be able to do: a binary that probes short of its own
|
|
8081
|
+
* claim is a source that changed under us, which is precisely what went
|
|
8082
|
+
* unnoticed for months when a static build turned out to carry no VAAPI at all
|
|
8083
|
+
* (D536).
|
|
8084
|
+
*/
|
|
8085
|
+
/** The pinned jellyfin-ffmpeg release. Changing this is the version change. */
|
|
8086
|
+
var FFMPEG_RELEASE = "8.1.2-5";
|
|
8087
|
+
var BASE = `https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v${FFMPEG_RELEASE}`;
|
|
8088
|
+
function portable(target) {
|
|
8089
|
+
return `${BASE}/jellyfin-ffmpeg_${FFMPEG_RELEASE}_portable_${target}-gpl.tar.xz`;
|
|
8090
|
+
}
|
|
8091
|
+
portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64");
|
|
8040
8092
|
/**
|
|
8041
8093
|
* LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
|
|
8042
8094
|
* valutatore puro del suo FRONTE.
|
|
@@ -14076,6 +14128,70 @@ var CompositionBlockStateSchema = object({
|
|
|
14076
14128
|
reason: string().optional(),
|
|
14077
14129
|
fields: array(CompositionFieldStateSchema)
|
|
14078
14130
|
});
|
|
14131
|
+
var NUMBER = {
|
|
14132
|
+
kind: "fixed",
|
|
14133
|
+
result: ["number"]
|
|
14134
|
+
};
|
|
14135
|
+
var BOOLEAN = {
|
|
14136
|
+
kind: "fixed",
|
|
14137
|
+
result: ["boolean"]
|
|
14138
|
+
};
|
|
14139
|
+
Object.freeze({
|
|
14140
|
+
min: NUMBER,
|
|
14141
|
+
max: NUMBER,
|
|
14142
|
+
abs: NUMBER,
|
|
14143
|
+
floor: NUMBER,
|
|
14144
|
+
ceil: NUMBER,
|
|
14145
|
+
sqrt: NUMBER,
|
|
14146
|
+
round: NUMBER,
|
|
14147
|
+
pow: NUMBER,
|
|
14148
|
+
clamp: NUMBER,
|
|
14149
|
+
avg: NUMBER,
|
|
14150
|
+
sum: NUMBER,
|
|
14151
|
+
age: NUMBER,
|
|
14152
|
+
convert: NUMBER,
|
|
14153
|
+
coalesce: { kind: "coalesce" },
|
|
14154
|
+
any: BOOLEAN,
|
|
14155
|
+
all: BOOLEAN,
|
|
14156
|
+
within: BOOLEAN,
|
|
14157
|
+
latest: {
|
|
14158
|
+
kind: "fixed",
|
|
14159
|
+
result: ["number", "null"]
|
|
14160
|
+
},
|
|
14161
|
+
number: {
|
|
14162
|
+
kind: "fixed",
|
|
14163
|
+
result: ["number", "null"]
|
|
14164
|
+
},
|
|
14165
|
+
rose: {
|
|
14166
|
+
kind: "fixed",
|
|
14167
|
+
result: ["number", "null"]
|
|
14168
|
+
},
|
|
14169
|
+
count: NUMBER
|
|
14170
|
+
});
|
|
14171
|
+
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
14172
|
+
/**
|
|
14173
|
+
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
14174
|
+
* device of that type, or an existing one that lacks them, can be given. The
|
|
14175
|
+
* blockers are the validator's own refusal codes, and a spec compares this
|
|
14176
|
+
* listing with `planComposition` for every capability — the editor can never
|
|
14177
|
+
* offer a feature the save would refuse for the same reason.
|
|
14178
|
+
*/
|
|
14179
|
+
var ComposableFieldSchema = object({
|
|
14180
|
+
path: string(),
|
|
14181
|
+
kind: CompositionFieldKindSchema,
|
|
14182
|
+
nullable: boolean(),
|
|
14183
|
+
required: boolean(),
|
|
14184
|
+
enumValues: array(string()).optional()
|
|
14185
|
+
});
|
|
14186
|
+
var ComposableCapabilitiesSchema = object({ caps: array(object({
|
|
14187
|
+
cap: string(),
|
|
14188
|
+
fields: array(ComposableFieldSchema),
|
|
14189
|
+
/** Present for an item-array cap (`consumables.items`). */
|
|
14190
|
+
itemArray: object({
|
|
14191
|
+
path: string(),
|
|
14192
|
+
keyField: string()
|
|
14193
|
+
}).optional()
|
|
14194
|
+
})) });
|
|
14079
14195
|
/** Where a block runs. The operator chooses — a block driving a device on an
|
|
14080
14196
|
* agent is the reason placement is not fixed to the hub. */
|
|
14081
14197
|
var CoreBlockPlacementSchema = union([literal("hub"), string().min(1)]);
|
|
@@ -14240,7 +14356,7 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
14240
14356
|
}), method(object({ blockId: string() }), CompositionGetReadSchema, { auth: "admin" }), method(object({}), CompositionListReadSchema, { auth: "admin" }), method(object({
|
|
14241
14357
|
addonId: string().min(1),
|
|
14242
14358
|
stableId: string().min(1)
|
|
14243
|
-
}), CompositionCustomizationReadSchema, { auth: "admin" }), method(object({
|
|
14359
|
+
}), CompositionCustomizationReadSchema, { auth: "admin" }), method(object({ deviceType: _enum(DeviceType) }), ComposableCapabilitiesSchema, { auth: "admin" }), method(object({
|
|
14244
14360
|
composition: CompositionSchema,
|
|
14245
14361
|
blockId: string().optional()
|
|
14246
14362
|
}), CompositionValidationSchema, {
|
|
@@ -25698,6 +25814,16 @@ var NodeInferenceDevicesSchema = object({
|
|
|
25698
25814
|
* Strictly the control plane: never sees a frame, never calls inference,
|
|
25699
25815
|
* never drains queues. The runner does that work locally on each agent.
|
|
25700
25816
|
*/
|
|
25817
|
+
/**
|
|
25818
|
+
* The input of `setCameraSensors` (G5, D676): the ONE typed write path of a
|
|
25819
|
+
* camera's attached sensors (D672). `config: null` removes the attachment —
|
|
25820
|
+
* the camera reads `not-configured` again, never `unreadable`.
|
|
25821
|
+
*/
|
|
25822
|
+
var SetCameraSensorsInputSchema = object({
|
|
25823
|
+
deviceId: number().int().nonnegative(),
|
|
25824
|
+
/** `null` removes the attachment: the camera goes back to `not-configured`. */
|
|
25825
|
+
config: CameraSensorsConfigSchema.nullable()
|
|
25826
|
+
});
|
|
25701
25827
|
var pipelineOrchestratorCapability = {
|
|
25702
25828
|
name: "pipeline-orchestrator",
|
|
25703
25829
|
scope: "system",
|
|
@@ -26163,6 +26289,18 @@ var pipelineOrchestratorCapability = {
|
|
|
26163
26289
|
*/
|
|
26164
26290
|
getCameraStatuses: method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()),
|
|
26165
26291
|
/**
|
|
26292
|
+
* Attach sensors to a camera, or detach them with `config: null` (D672
|
|
26293
|
+
* configuration, G5/D676). Written to the orchestrator's per-device store
|
|
26294
|
+
* under the same key `applyDeviceSettingsPatch` writes, and announced as a
|
|
26295
|
+
* settings change so the camera-sensors bridge refreshes. Refused by name
|
|
26296
|
+
* — nothing written — for a device that cannot be read, does not exist,
|
|
26297
|
+
* is not a camera, or names itself as its own sensor.
|
|
26298
|
+
*/
|
|
26299
|
+
setCameraSensors: method(SetCameraSensorsInputSchema, object({ success: literal(true) }), {
|
|
26300
|
+
kind: "mutation",
|
|
26301
|
+
auth: "admin"
|
|
26302
|
+
}),
|
|
26303
|
+
/**
|
|
26166
26304
|
* Disk-wins fleet reconcile after a recordings wipe. Starts the walk in
|
|
26167
26305
|
* the addon process and returns immediately with the job snapshot — the
|
|
26168
26306
|
* work outlives the tRPC/UDS 60s timeout. Poll `getReconcileFromDiskStatus`.
|
|
@@ -37192,47 +37330,6 @@ var zoneRulesCapability = {
|
|
|
37192
37330
|
*/
|
|
37193
37331
|
durability: "restored"
|
|
37194
37332
|
};
|
|
37195
|
-
var NUMBER = {
|
|
37196
|
-
kind: "fixed",
|
|
37197
|
-
result: ["number"]
|
|
37198
|
-
};
|
|
37199
|
-
var BOOLEAN = {
|
|
37200
|
-
kind: "fixed",
|
|
37201
|
-
result: ["boolean"]
|
|
37202
|
-
};
|
|
37203
|
-
Object.freeze({
|
|
37204
|
-
min: NUMBER,
|
|
37205
|
-
max: NUMBER,
|
|
37206
|
-
abs: NUMBER,
|
|
37207
|
-
floor: NUMBER,
|
|
37208
|
-
ceil: NUMBER,
|
|
37209
|
-
sqrt: NUMBER,
|
|
37210
|
-
round: NUMBER,
|
|
37211
|
-
pow: NUMBER,
|
|
37212
|
-
clamp: NUMBER,
|
|
37213
|
-
avg: NUMBER,
|
|
37214
|
-
sum: NUMBER,
|
|
37215
|
-
age: NUMBER,
|
|
37216
|
-
convert: NUMBER,
|
|
37217
|
-
coalesce: { kind: "coalesce" },
|
|
37218
|
-
any: BOOLEAN,
|
|
37219
|
-
all: BOOLEAN,
|
|
37220
|
-
within: BOOLEAN,
|
|
37221
|
-
latest: {
|
|
37222
|
-
kind: "fixed",
|
|
37223
|
-
result: ["number", "null"]
|
|
37224
|
-
},
|
|
37225
|
-
number: {
|
|
37226
|
-
kind: "fixed",
|
|
37227
|
-
result: ["number", "null"]
|
|
37228
|
-
},
|
|
37229
|
-
rose: {
|
|
37230
|
-
kind: "fixed",
|
|
37231
|
-
result: ["number", "null"]
|
|
37232
|
-
},
|
|
37233
|
-
count: NUMBER
|
|
37234
|
-
});
|
|
37235
|
-
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
37236
37333
|
/**
|
|
37237
37334
|
* Accessory device helpers — shared across drivers.
|
|
37238
37335
|
*
|
|
@@ -39683,6 +39780,12 @@ Object.freeze({
|
|
|
39683
39780
|
addonId: null,
|
|
39684
39781
|
access: "view"
|
|
39685
39782
|
},
|
|
39783
|
+
"coreBlocks.listComposableCaps": {
|
|
39784
|
+
capName: "core-blocks",
|
|
39785
|
+
capScope: "system",
|
|
39786
|
+
addonId: null,
|
|
39787
|
+
access: "view"
|
|
39788
|
+
},
|
|
39686
39789
|
"coreBlocks.listCompositions": {
|
|
39687
39790
|
capName: "core-blocks",
|
|
39688
39791
|
capScope: "system",
|
|
@@ -43367,6 +43470,12 @@ Object.freeze({
|
|
|
43367
43470
|
addonId: null,
|
|
43368
43471
|
access: "create"
|
|
43369
43472
|
},
|
|
43473
|
+
"pipelineOrchestrator.setCameraSensors": {
|
|
43474
|
+
capName: "pipeline-orchestrator",
|
|
43475
|
+
capScope: "system",
|
|
43476
|
+
addonId: null,
|
|
43477
|
+
access: "create"
|
|
43478
|
+
},
|
|
43370
43479
|
"pipelineOrchestrator.setCameraStepOverride": {
|
|
43371
43480
|
capName: "pipeline-orchestrator",
|
|
43372
43481
|
capScope: "system",
|
|
@@ -47077,6 +47186,11 @@ Object.freeze({
|
|
|
47077
47186
|
form: "single",
|
|
47078
47187
|
optional: false
|
|
47079
47188
|
}],
|
|
47189
|
+
"pipelineOrchestrator.setCameraSensors": [{
|
|
47190
|
+
name: "deviceId",
|
|
47191
|
+
form: "single",
|
|
47192
|
+
optional: false
|
|
47193
|
+
}],
|
|
47080
47194
|
"pipelineOrchestrator.setCameraStepOverride": [{
|
|
47081
47195
|
name: "deviceId",
|
|
47082
47196
|
form: "single",
|
|
@@ -49698,6 +49812,52 @@ var AudioSubscriptionController = class {
|
|
|
49698
49812
|
}
|
|
49699
49813
|
};
|
|
49700
49814
|
//#endregion
|
|
49815
|
+
//#region src/camera-sensors/write.ts
|
|
49816
|
+
/**
|
|
49817
|
+
* The ONE typed write path of a camera's attached sensors (D672 config,
|
|
49818
|
+
* G5/D676) — `pipelineOrchestrator.setCameraSensors`.
|
|
49819
|
+
*
|
|
49820
|
+
* The input is already validated by the cap schema at the router. Here it is
|
|
49821
|
+
* refused BY NAME, and logged with the device tag, before anything is written
|
|
49822
|
+
* (D391): a device that could not be read (a failed read changes nothing,
|
|
49823
|
+
* D49), an unknown device, a non-camera, a camera named as its own sensor.
|
|
49824
|
+
* The value is then handed to `writeDeviceStore` under the CS-1 key, which the
|
|
49825
|
+
* addon binds to `applyDeviceSettingsPatch` — so the D672 patch gate, the
|
|
49826
|
+
* PARSED value, the store and the settings-updated event that refreshes the
|
|
49827
|
+
* bridge's resolver are ONE path, never a copy of it. `null` clears: the
|
|
49828
|
+
* device store keeps a null key, and `parseCameraSensorsSetting` reads it as
|
|
49829
|
+
* not-configured.
|
|
49830
|
+
*/
|
|
49831
|
+
function refuse(deps, deviceId, reason) {
|
|
49832
|
+
deps.logger.warn("camera sensors write refused", {
|
|
49833
|
+
tags: { deviceId },
|
|
49834
|
+
meta: { reason }
|
|
49835
|
+
});
|
|
49836
|
+
throw new Error(`pipeline-orchestrator: camera sensors of ${deviceId} not saved — ${reason}`);
|
|
49837
|
+
}
|
|
49838
|
+
async function readOrRefuse(deps, deviceId) {
|
|
49839
|
+
let device;
|
|
49840
|
+
try {
|
|
49841
|
+
device = await deps.readDevice(deviceId);
|
|
49842
|
+
} catch (err) {
|
|
49843
|
+
return refuse(deps, deviceId, `could not read device ${deviceId}: ${errMsg$1(err)}`);
|
|
49844
|
+
}
|
|
49845
|
+
if (device === null) return refuse(deps, deviceId, `no device ${deviceId}`);
|
|
49846
|
+
return device;
|
|
49847
|
+
}
|
|
49848
|
+
async function writeCameraSensors(deps, input) {
|
|
49849
|
+
const { deviceId, config } = input;
|
|
49850
|
+
const device = await readOrRefuse(deps, deviceId);
|
|
49851
|
+
if (device.type !== DeviceType.Camera) return refuse(deps, deviceId, `device ${deviceId} is not a camera`);
|
|
49852
|
+
if (config !== null && config.sensors.some((s) => s.addonId === device.addonId && s.stableId === device.stableId)) return refuse(deps, deviceId, "a camera cannot be its own sensor");
|
|
49853
|
+
await deps.writeDeviceStore(deviceId, { [CAMERA_SENSORS_SETTINGS_KEY]: config });
|
|
49854
|
+
deps.logger.info("camera sensors saved", {
|
|
49855
|
+
tags: { deviceId },
|
|
49856
|
+
meta: { sensors: config === null ? 0 : config.sensors.length }
|
|
49857
|
+
});
|
|
49858
|
+
return { success: true };
|
|
49859
|
+
}
|
|
49860
|
+
//#endregion
|
|
49701
49861
|
//#region src/disk-reconcile-fleet.ts
|
|
49702
49862
|
async function reconcileFleetFromDisk(deps) {
|
|
49703
49863
|
const deviceIds = await deps.listDeviceIds();
|
|
@@ -66215,6 +66375,37 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
66215
66375
|
return this.deviceConfig.applyDeviceSettingsPatch(input);
|
|
66216
66376
|
}
|
|
66217
66377
|
/**
|
|
66378
|
+
* The one typed write path of a camera's attached sensors (G5, D676) — see
|
|
66379
|
+
* `camera-sensors/write.ts`. Its refusals run first; the store write then
|
|
66380
|
+
* goes THROUGH `applyDeviceSettingsPatch`, so the D672 patch gate, the
|
|
66381
|
+
* parsed value, the key and the `OrchestrationSettingsUpdated` event are the
|
|
66382
|
+
* one path a camera configured through the device page already takes.
|
|
66383
|
+
*/
|
|
66384
|
+
async setCameraSensors(input) {
|
|
66385
|
+
const ctx = this.ctx;
|
|
66386
|
+
const deviceConfig = this.deviceConfig;
|
|
66387
|
+
if (deviceConfig === null) throw new Error("pipeline-orchestrator: not initialised — camera sensors not saved");
|
|
66388
|
+
const api = ctx.api;
|
|
66389
|
+
return writeCameraSensors({
|
|
66390
|
+
readDevice: async (deviceId) => {
|
|
66391
|
+
if (!api) throw new Error("the device registry is not reachable");
|
|
66392
|
+
const dev = await api.deviceManager.getDevice.query({ deviceId });
|
|
66393
|
+
return dev === null || dev === void 0 ? null : {
|
|
66394
|
+
type: dev.type,
|
|
66395
|
+
addonId: dev.addonId,
|
|
66396
|
+
stableId: dev.stableId
|
|
66397
|
+
};
|
|
66398
|
+
},
|
|
66399
|
+
writeDeviceStore: async (deviceId, patch) => {
|
|
66400
|
+
await deviceConfig.applyDeviceSettingsPatch({
|
|
66401
|
+
deviceId,
|
|
66402
|
+
patch
|
|
66403
|
+
});
|
|
66404
|
+
},
|
|
66405
|
+
logger: ctx.logger.child("camera-sensors")
|
|
66406
|
+
}, input);
|
|
66407
|
+
}
|
|
66408
|
+
/**
|
|
66218
66409
|
* Apply the subset of global settings that affect runtime behaviour
|
|
66219
66410
|
* (balancer thresholds, broker timeout, failover policy) into the
|
|
66220
66411
|
* in-memory state so subsequent `balance()` / `handleNodeDisconnect`
|
package/dist/index.mjs
CHANGED
|
@@ -8008,7 +8008,59 @@ function errMsg$1(err) {
|
|
|
8008
8008
|
return String(err);
|
|
8009
8009
|
}
|
|
8010
8010
|
//#endregion
|
|
8011
|
-
//#region ../types/dist/composition-
|
|
8011
|
+
//#region ../types/dist/composition-Dq42XWp0.mjs
|
|
8012
|
+
/**
|
|
8013
|
+
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
8014
|
+
*
|
|
8015
|
+
* ## Why a download, on every node, including the container
|
|
8016
|
+
*
|
|
8017
|
+
* The version of ffmpeg used to be CODE: baked into the image, changeable only
|
|
8018
|
+
* by rebuilding and rolling it. Now it is DATA — this file states the release,
|
|
8019
|
+
* and a node that boots with a different one downloads it. Changing ffmpeg is a
|
|
8020
|
+
* framework bump, not an image rebuild.
|
|
8021
|
+
*
|
|
8022
|
+
* It also settles the licence question by removing it. FFmpeg's licence is
|
|
8023
|
+
* chosen at compile time: the core is LGPL 2.1+, `--enable-gpl` makes it GPL
|
|
8024
|
+
* (for x264/x265), `--enable-version3` makes that v3, and `--enable-nonfree`
|
|
8025
|
+
* makes the result undistributable. These are GPL v3 builds, and we do not
|
|
8026
|
+
* distribute them: the user's own node fetches the archive from the project
|
|
8027
|
+
* that publishes it, so we convey no GPL work and inherit none of the
|
|
8028
|
+
* obligations that come with conveying one. That is the whole reason the image
|
|
8029
|
+
* no longer installs ffmpeg at all — not a technical preference.
|
|
8030
|
+
*
|
|
8031
|
+
* ## Why jellyfin-ffmpeg
|
|
8032
|
+
*
|
|
8033
|
+
* One vendor covering linux-amd64, linux-arm64, darwin-arm64 and darwin-x64
|
|
8034
|
+
* from one release, with the widest hardware surface of anything maintained:
|
|
8035
|
+
* VAAPI, QSV, NVENC/NVDEC, AMF, Vulkan, OpenCL and DRM on amd64, plus Rockchip
|
|
8036
|
+
* MPP on arm64. It reaches the vendor libraries through dlopen trampolines,
|
|
8037
|
+
* so the binary is not linked against a driver stack it may not find.
|
|
8038
|
+
*
|
|
8039
|
+
* **What it still needs from the host, measured 2026-09-19.** On the Unraid
|
|
8040
|
+
* HOST, which has no libva, `-vaapi_device` aborts the process outright —
|
|
8041
|
+
* `implib-gen: libva-drm.so.2: failed to load library ... Assertion '0 &&
|
|
8042
|
+
* "Assertion in generated code"' failed`. Inside our container, which installs
|
|
8043
|
+
* libva plus the iHD driver, the same command encodes: verified end to end with
|
|
8044
|
+
* `testsrc → format=nv12,hwupload → h264_vaapi`. So the image drops ffmpeg and
|
|
8045
|
+
* KEEPS the Intel media stack; the drivers are the part a binary cannot bring.
|
|
8046
|
+
*
|
|
8047
|
+
* ## Why each artifact carries a checksum and a claim
|
|
8048
|
+
*
|
|
8049
|
+
* The download is now the only source, so `sha256` is verified before anything
|
|
8050
|
+
* is extracted — jellyfin publishes no checksums on its release assets, so
|
|
8051
|
+
* these are ours, taken at the version bump. And `expectedHwaccels` is what the
|
|
8052
|
+
* build is supposed to be able to do: a binary that probes short of its own
|
|
8053
|
+
* claim is a source that changed under us, which is precisely what went
|
|
8054
|
+
* unnoticed for months when a static build turned out to carry no VAAPI at all
|
|
8055
|
+
* (D536).
|
|
8056
|
+
*/
|
|
8057
|
+
/** The pinned jellyfin-ffmpeg release. Changing this is the version change. */
|
|
8058
|
+
var FFMPEG_RELEASE = "8.1.2-5";
|
|
8059
|
+
var BASE = `https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v${FFMPEG_RELEASE}`;
|
|
8060
|
+
function portable(target) {
|
|
8061
|
+
return `${BASE}/jellyfin-ffmpeg_${FFMPEG_RELEASE}_portable_${target}-gpl.tar.xz`;
|
|
8062
|
+
}
|
|
8063
|
+
portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64");
|
|
8012
8064
|
/**
|
|
8013
8065
|
* LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
|
|
8014
8066
|
* valutatore puro del suo FRONTE.
|
|
@@ -14048,6 +14100,70 @@ var CompositionBlockStateSchema = object({
|
|
|
14048
14100
|
reason: string().optional(),
|
|
14049
14101
|
fields: array(CompositionFieldStateSchema)
|
|
14050
14102
|
});
|
|
14103
|
+
var NUMBER = {
|
|
14104
|
+
kind: "fixed",
|
|
14105
|
+
result: ["number"]
|
|
14106
|
+
};
|
|
14107
|
+
var BOOLEAN = {
|
|
14108
|
+
kind: "fixed",
|
|
14109
|
+
result: ["boolean"]
|
|
14110
|
+
};
|
|
14111
|
+
Object.freeze({
|
|
14112
|
+
min: NUMBER,
|
|
14113
|
+
max: NUMBER,
|
|
14114
|
+
abs: NUMBER,
|
|
14115
|
+
floor: NUMBER,
|
|
14116
|
+
ceil: NUMBER,
|
|
14117
|
+
sqrt: NUMBER,
|
|
14118
|
+
round: NUMBER,
|
|
14119
|
+
pow: NUMBER,
|
|
14120
|
+
clamp: NUMBER,
|
|
14121
|
+
avg: NUMBER,
|
|
14122
|
+
sum: NUMBER,
|
|
14123
|
+
age: NUMBER,
|
|
14124
|
+
convert: NUMBER,
|
|
14125
|
+
coalesce: { kind: "coalesce" },
|
|
14126
|
+
any: BOOLEAN,
|
|
14127
|
+
all: BOOLEAN,
|
|
14128
|
+
within: BOOLEAN,
|
|
14129
|
+
latest: {
|
|
14130
|
+
kind: "fixed",
|
|
14131
|
+
result: ["number", "null"]
|
|
14132
|
+
},
|
|
14133
|
+
number: {
|
|
14134
|
+
kind: "fixed",
|
|
14135
|
+
result: ["number", "null"]
|
|
14136
|
+
},
|
|
14137
|
+
rose: {
|
|
14138
|
+
kind: "fixed",
|
|
14139
|
+
result: ["number", "null"]
|
|
14140
|
+
},
|
|
14141
|
+
count: NUMBER
|
|
14142
|
+
});
|
|
14143
|
+
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
14144
|
+
/**
|
|
14145
|
+
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
14146
|
+
* device of that type, or an existing one that lacks them, can be given. The
|
|
14147
|
+
* blockers are the validator's own refusal codes, and a spec compares this
|
|
14148
|
+
* listing with `planComposition` for every capability — the editor can never
|
|
14149
|
+
* offer a feature the save would refuse for the same reason.
|
|
14150
|
+
*/
|
|
14151
|
+
var ComposableFieldSchema = object({
|
|
14152
|
+
path: string(),
|
|
14153
|
+
kind: CompositionFieldKindSchema,
|
|
14154
|
+
nullable: boolean(),
|
|
14155
|
+
required: boolean(),
|
|
14156
|
+
enumValues: array(string()).optional()
|
|
14157
|
+
});
|
|
14158
|
+
var ComposableCapabilitiesSchema = object({ caps: array(object({
|
|
14159
|
+
cap: string(),
|
|
14160
|
+
fields: array(ComposableFieldSchema),
|
|
14161
|
+
/** Present for an item-array cap (`consumables.items`). */
|
|
14162
|
+
itemArray: object({
|
|
14163
|
+
path: string(),
|
|
14164
|
+
keyField: string()
|
|
14165
|
+
}).optional()
|
|
14166
|
+
})) });
|
|
14051
14167
|
/** Where a block runs. The operator chooses — a block driving a device on an
|
|
14052
14168
|
* agent is the reason placement is not fixed to the hub. */
|
|
14053
14169
|
var CoreBlockPlacementSchema = union([literal("hub"), string().min(1)]);
|
|
@@ -14212,7 +14328,7 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
14212
14328
|
}), method(object({ blockId: string() }), CompositionGetReadSchema, { auth: "admin" }), method(object({}), CompositionListReadSchema, { auth: "admin" }), method(object({
|
|
14213
14329
|
addonId: string().min(1),
|
|
14214
14330
|
stableId: string().min(1)
|
|
14215
|
-
}), CompositionCustomizationReadSchema, { auth: "admin" }), method(object({
|
|
14331
|
+
}), CompositionCustomizationReadSchema, { auth: "admin" }), method(object({ deviceType: _enum(DeviceType) }), ComposableCapabilitiesSchema, { auth: "admin" }), method(object({
|
|
14216
14332
|
composition: CompositionSchema,
|
|
14217
14333
|
blockId: string().optional()
|
|
14218
14334
|
}), CompositionValidationSchema, {
|
|
@@ -25670,6 +25786,16 @@ var NodeInferenceDevicesSchema = object({
|
|
|
25670
25786
|
* Strictly the control plane: never sees a frame, never calls inference,
|
|
25671
25787
|
* never drains queues. The runner does that work locally on each agent.
|
|
25672
25788
|
*/
|
|
25789
|
+
/**
|
|
25790
|
+
* The input of `setCameraSensors` (G5, D676): the ONE typed write path of a
|
|
25791
|
+
* camera's attached sensors (D672). `config: null` removes the attachment —
|
|
25792
|
+
* the camera reads `not-configured` again, never `unreadable`.
|
|
25793
|
+
*/
|
|
25794
|
+
var SetCameraSensorsInputSchema = object({
|
|
25795
|
+
deviceId: number().int().nonnegative(),
|
|
25796
|
+
/** `null` removes the attachment: the camera goes back to `not-configured`. */
|
|
25797
|
+
config: CameraSensorsConfigSchema.nullable()
|
|
25798
|
+
});
|
|
25673
25799
|
var pipelineOrchestratorCapability = {
|
|
25674
25800
|
name: "pipeline-orchestrator",
|
|
25675
25801
|
scope: "system",
|
|
@@ -26135,6 +26261,18 @@ var pipelineOrchestratorCapability = {
|
|
|
26135
26261
|
*/
|
|
26136
26262
|
getCameraStatuses: method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()),
|
|
26137
26263
|
/**
|
|
26264
|
+
* Attach sensors to a camera, or detach them with `config: null` (D672
|
|
26265
|
+
* configuration, G5/D676). Written to the orchestrator's per-device store
|
|
26266
|
+
* under the same key `applyDeviceSettingsPatch` writes, and announced as a
|
|
26267
|
+
* settings change so the camera-sensors bridge refreshes. Refused by name
|
|
26268
|
+
* — nothing written — for a device that cannot be read, does not exist,
|
|
26269
|
+
* is not a camera, or names itself as its own sensor.
|
|
26270
|
+
*/
|
|
26271
|
+
setCameraSensors: method(SetCameraSensorsInputSchema, object({ success: literal(true) }), {
|
|
26272
|
+
kind: "mutation",
|
|
26273
|
+
auth: "admin"
|
|
26274
|
+
}),
|
|
26275
|
+
/**
|
|
26138
26276
|
* Disk-wins fleet reconcile after a recordings wipe. Starts the walk in
|
|
26139
26277
|
* the addon process and returns immediately with the job snapshot — the
|
|
26140
26278
|
* work outlives the tRPC/UDS 60s timeout. Poll `getReconcileFromDiskStatus`.
|
|
@@ -37164,47 +37302,6 @@ var zoneRulesCapability = {
|
|
|
37164
37302
|
*/
|
|
37165
37303
|
durability: "restored"
|
|
37166
37304
|
};
|
|
37167
|
-
var NUMBER = {
|
|
37168
|
-
kind: "fixed",
|
|
37169
|
-
result: ["number"]
|
|
37170
|
-
};
|
|
37171
|
-
var BOOLEAN = {
|
|
37172
|
-
kind: "fixed",
|
|
37173
|
-
result: ["boolean"]
|
|
37174
|
-
};
|
|
37175
|
-
Object.freeze({
|
|
37176
|
-
min: NUMBER,
|
|
37177
|
-
max: NUMBER,
|
|
37178
|
-
abs: NUMBER,
|
|
37179
|
-
floor: NUMBER,
|
|
37180
|
-
ceil: NUMBER,
|
|
37181
|
-
sqrt: NUMBER,
|
|
37182
|
-
round: NUMBER,
|
|
37183
|
-
pow: NUMBER,
|
|
37184
|
-
clamp: NUMBER,
|
|
37185
|
-
avg: NUMBER,
|
|
37186
|
-
sum: NUMBER,
|
|
37187
|
-
age: NUMBER,
|
|
37188
|
-
convert: NUMBER,
|
|
37189
|
-
coalesce: { kind: "coalesce" },
|
|
37190
|
-
any: BOOLEAN,
|
|
37191
|
-
all: BOOLEAN,
|
|
37192
|
-
within: BOOLEAN,
|
|
37193
|
-
latest: {
|
|
37194
|
-
kind: "fixed",
|
|
37195
|
-
result: ["number", "null"]
|
|
37196
|
-
},
|
|
37197
|
-
number: {
|
|
37198
|
-
kind: "fixed",
|
|
37199
|
-
result: ["number", "null"]
|
|
37200
|
-
},
|
|
37201
|
-
rose: {
|
|
37202
|
-
kind: "fixed",
|
|
37203
|
-
result: ["number", "null"]
|
|
37204
|
-
},
|
|
37205
|
-
count: NUMBER
|
|
37206
|
-
});
|
|
37207
|
-
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
37208
37305
|
/**
|
|
37209
37306
|
* Accessory device helpers — shared across drivers.
|
|
37210
37307
|
*
|
|
@@ -39655,6 +39752,12 @@ Object.freeze({
|
|
|
39655
39752
|
addonId: null,
|
|
39656
39753
|
access: "view"
|
|
39657
39754
|
},
|
|
39755
|
+
"coreBlocks.listComposableCaps": {
|
|
39756
|
+
capName: "core-blocks",
|
|
39757
|
+
capScope: "system",
|
|
39758
|
+
addonId: null,
|
|
39759
|
+
access: "view"
|
|
39760
|
+
},
|
|
39658
39761
|
"coreBlocks.listCompositions": {
|
|
39659
39762
|
capName: "core-blocks",
|
|
39660
39763
|
capScope: "system",
|
|
@@ -43339,6 +43442,12 @@ Object.freeze({
|
|
|
43339
43442
|
addonId: null,
|
|
43340
43443
|
access: "create"
|
|
43341
43444
|
},
|
|
43445
|
+
"pipelineOrchestrator.setCameraSensors": {
|
|
43446
|
+
capName: "pipeline-orchestrator",
|
|
43447
|
+
capScope: "system",
|
|
43448
|
+
addonId: null,
|
|
43449
|
+
access: "create"
|
|
43450
|
+
},
|
|
43342
43451
|
"pipelineOrchestrator.setCameraStepOverride": {
|
|
43343
43452
|
capName: "pipeline-orchestrator",
|
|
43344
43453
|
capScope: "system",
|
|
@@ -47049,6 +47158,11 @@ Object.freeze({
|
|
|
47049
47158
|
form: "single",
|
|
47050
47159
|
optional: false
|
|
47051
47160
|
}],
|
|
47161
|
+
"pipelineOrchestrator.setCameraSensors": [{
|
|
47162
|
+
name: "deviceId",
|
|
47163
|
+
form: "single",
|
|
47164
|
+
optional: false
|
|
47165
|
+
}],
|
|
47052
47166
|
"pipelineOrchestrator.setCameraStepOverride": [{
|
|
47053
47167
|
name: "deviceId",
|
|
47054
47168
|
form: "single",
|
|
@@ -49670,6 +49784,52 @@ var AudioSubscriptionController = class {
|
|
|
49670
49784
|
}
|
|
49671
49785
|
};
|
|
49672
49786
|
//#endregion
|
|
49787
|
+
//#region src/camera-sensors/write.ts
|
|
49788
|
+
/**
|
|
49789
|
+
* The ONE typed write path of a camera's attached sensors (D672 config,
|
|
49790
|
+
* G5/D676) — `pipelineOrchestrator.setCameraSensors`.
|
|
49791
|
+
*
|
|
49792
|
+
* The input is already validated by the cap schema at the router. Here it is
|
|
49793
|
+
* refused BY NAME, and logged with the device tag, before anything is written
|
|
49794
|
+
* (D391): a device that could not be read (a failed read changes nothing,
|
|
49795
|
+
* D49), an unknown device, a non-camera, a camera named as its own sensor.
|
|
49796
|
+
* The value is then handed to `writeDeviceStore` under the CS-1 key, which the
|
|
49797
|
+
* addon binds to `applyDeviceSettingsPatch` — so the D672 patch gate, the
|
|
49798
|
+
* PARSED value, the store and the settings-updated event that refreshes the
|
|
49799
|
+
* bridge's resolver are ONE path, never a copy of it. `null` clears: the
|
|
49800
|
+
* device store keeps a null key, and `parseCameraSensorsSetting` reads it as
|
|
49801
|
+
* not-configured.
|
|
49802
|
+
*/
|
|
49803
|
+
function refuse(deps, deviceId, reason) {
|
|
49804
|
+
deps.logger.warn("camera sensors write refused", {
|
|
49805
|
+
tags: { deviceId },
|
|
49806
|
+
meta: { reason }
|
|
49807
|
+
});
|
|
49808
|
+
throw new Error(`pipeline-orchestrator: camera sensors of ${deviceId} not saved — ${reason}`);
|
|
49809
|
+
}
|
|
49810
|
+
async function readOrRefuse(deps, deviceId) {
|
|
49811
|
+
let device;
|
|
49812
|
+
try {
|
|
49813
|
+
device = await deps.readDevice(deviceId);
|
|
49814
|
+
} catch (err) {
|
|
49815
|
+
return refuse(deps, deviceId, `could not read device ${deviceId}: ${errMsg$1(err)}`);
|
|
49816
|
+
}
|
|
49817
|
+
if (device === null) return refuse(deps, deviceId, `no device ${deviceId}`);
|
|
49818
|
+
return device;
|
|
49819
|
+
}
|
|
49820
|
+
async function writeCameraSensors(deps, input) {
|
|
49821
|
+
const { deviceId, config } = input;
|
|
49822
|
+
const device = await readOrRefuse(deps, deviceId);
|
|
49823
|
+
if (device.type !== DeviceType.Camera) return refuse(deps, deviceId, `device ${deviceId} is not a camera`);
|
|
49824
|
+
if (config !== null && config.sensors.some((s) => s.addonId === device.addonId && s.stableId === device.stableId)) return refuse(deps, deviceId, "a camera cannot be its own sensor");
|
|
49825
|
+
await deps.writeDeviceStore(deviceId, { [CAMERA_SENSORS_SETTINGS_KEY]: config });
|
|
49826
|
+
deps.logger.info("camera sensors saved", {
|
|
49827
|
+
tags: { deviceId },
|
|
49828
|
+
meta: { sensors: config === null ? 0 : config.sensors.length }
|
|
49829
|
+
});
|
|
49830
|
+
return { success: true };
|
|
49831
|
+
}
|
|
49832
|
+
//#endregion
|
|
49673
49833
|
//#region src/disk-reconcile-fleet.ts
|
|
49674
49834
|
async function reconcileFleetFromDisk(deps) {
|
|
49675
49835
|
const deviceIds = await deps.listDeviceIds();
|
|
@@ -66187,6 +66347,37 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
66187
66347
|
return this.deviceConfig.applyDeviceSettingsPatch(input);
|
|
66188
66348
|
}
|
|
66189
66349
|
/**
|
|
66350
|
+
* The one typed write path of a camera's attached sensors (G5, D676) — see
|
|
66351
|
+
* `camera-sensors/write.ts`. Its refusals run first; the store write then
|
|
66352
|
+
* goes THROUGH `applyDeviceSettingsPatch`, so the D672 patch gate, the
|
|
66353
|
+
* parsed value, the key and the `OrchestrationSettingsUpdated` event are the
|
|
66354
|
+
* one path a camera configured through the device page already takes.
|
|
66355
|
+
*/
|
|
66356
|
+
async setCameraSensors(input) {
|
|
66357
|
+
const ctx = this.ctx;
|
|
66358
|
+
const deviceConfig = this.deviceConfig;
|
|
66359
|
+
if (deviceConfig === null) throw new Error("pipeline-orchestrator: not initialised — camera sensors not saved");
|
|
66360
|
+
const api = ctx.api;
|
|
66361
|
+
return writeCameraSensors({
|
|
66362
|
+
readDevice: async (deviceId) => {
|
|
66363
|
+
if (!api) throw new Error("the device registry is not reachable");
|
|
66364
|
+
const dev = await api.deviceManager.getDevice.query({ deviceId });
|
|
66365
|
+
return dev === null || dev === void 0 ? null : {
|
|
66366
|
+
type: dev.type,
|
|
66367
|
+
addonId: dev.addonId,
|
|
66368
|
+
stableId: dev.stableId
|
|
66369
|
+
};
|
|
66370
|
+
},
|
|
66371
|
+
writeDeviceStore: async (deviceId, patch) => {
|
|
66372
|
+
await deviceConfig.applyDeviceSettingsPatch({
|
|
66373
|
+
deviceId,
|
|
66374
|
+
patch
|
|
66375
|
+
});
|
|
66376
|
+
},
|
|
66377
|
+
logger: ctx.logger.child("camera-sensors")
|
|
66378
|
+
}, input);
|
|
66379
|
+
}
|
|
66380
|
+
/**
|
|
66190
66381
|
* Apply the subset of global settings that affect runtime behaviour
|
|
66191
66382
|
* (balancer thresholds, broker timeout, failover policy) into the
|
|
66192
66383
|
* in-memory state so subsequent `balance()` / `handleNodeDisconnect`
|