@camstack/addon-pipeline 1.1.18 → 1.1.19
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/audio-analyzer/index.js +1 -1
- package/dist/audio-analyzer/index.mjs +1 -1
- package/dist/audio-codec-nodeav/index.js +1 -1
- package/dist/audio-codec-nodeav/index.mjs +1 -1
- package/dist/decoder-ffmpeg/index.js +1179 -0
- package/dist/decoder-ffmpeg/index.mjs +1162 -0
- package/dist/detection-pipeline/index.js +1 -1
- package/dist/detection-pipeline/index.mjs +1 -1
- package/dist/{dist-CBcVKDyT.mjs → dist-7Yx2dmuV.mjs} +1 -0
- package/dist/{dist-CRzS7bK1.js → dist-BgoBMCez.js} +1 -0
- package/dist/ffmpeg-args-common-CASoNt42.js +68 -0
- package/dist/ffmpeg-args-common-c3p-nWtU.mjs +51 -0
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +1 -1
- package/dist/pipeline-runner/index.mjs +1 -1
- package/dist/recorder/index.js +1 -1
- package/dist/recorder/index.mjs +1 -1
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-Cu2RfLyo.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-w2pP0MYO.mjs} +1 -1
- package/dist/stream-broker/{hostInit-D2VENktw.mjs → hostInit-BUEbBinp.mjs} +1 -1
- package/dist/stream-broker/index.js +67 -64
- package/dist/stream-broker/index.mjs +57 -54
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-ohKbGSOY.js → MaskShapeCanvas-DI4BY7W2-CYmHXacX.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-m6kOetLh.js → MotionZonesSettings-NcxxQN8r-B-BkPQvX.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-BNbgX3su.js → PrivacyMaskSettings-APgPLF7p-DC3Jw85p.js} +1 -1
- package/embed-dist/assets/{index-CqqHOFtM.js → index-B0IQgci0.js} +4 -4
- package/embed-dist/index.html +1 -1
- package/package.json +8 -8
- package/dist/decoder-nodeav/index.js +0 -1393
- package/dist/decoder-nodeav/index.mjs +0 -1385
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { t as __require } from "../chunk-BdkLduGY.mjs";
|
|
2
|
-
import { A as nodePin, B as BaseAddon, D as maskUrlCredentials, G as asJsonObject, H as DeviceFeature, I as streamBrokerCapability, Q as parseProfileBrokerId, R as webrtcSessionCapability, U as DeviceType, V as CAM_PROFILE_ORDER, W as EventCategory, X as makeSourceBrokerId, Y as makeProfileBrokerId, c as EncodeProfileSchema, ct as object, dt as union, f as RingBuffer, it as discriminatedUnion, lt as record, m as addonWidgetsSourceCapability, ot as literal, q as createEvent, rt as boolean, st as number, tt as _enum, ut as string, v as cameraStreamsCapability, z as errMsg } from "../dist-
|
|
2
|
+
import { A as nodePin, B as BaseAddon, D as maskUrlCredentials, G as asJsonObject, H as DeviceFeature, I as streamBrokerCapability, Q as parseProfileBrokerId, R as webrtcSessionCapability, U as DeviceType, V as CAM_PROFILE_ORDER, W as EventCategory, X as makeSourceBrokerId, Y as makeProfileBrokerId, c as EncodeProfileSchema, ct as object, dt as union, f as RingBuffer, it as discriminatedUnion, lt as record, m as addonWidgetsSourceCapability, ot as literal, q as createEvent, rt as boolean, st as number, tt as _enum, ut as string, v as cameraStreamsCapability, z as errMsg } from "../dist-7Yx2dmuV.mjs";
|
|
3
3
|
import { t as createFileDataPlaneHandler } from "../model-download-service-C-IHWnXx-3Mmeob3l.mjs";
|
|
4
|
+
import { n as logBannerArgs, r as FrameDropper, t as audioEncoderArgs } from "../ffmpeg-args-common-c3p-nWtU.mjs";
|
|
4
5
|
import { t as DecodeRuntime } from "../codec-runtime-BsqlEjPi.mjs";
|
|
5
6
|
import { createRequire } from "node:module";
|
|
6
7
|
import * as fs from "node:fs";
|
|
@@ -304,6 +305,31 @@ var DecoderSessionProxy = class {
|
|
|
304
305
|
* broker supplies a `pullModeUrl` and the plane calls `openStream` once the
|
|
305
306
|
* session exists.
|
|
306
307
|
*/
|
|
308
|
+
/**
|
|
309
|
+
* Collapse a hierarchical Moleculer nodeID (`hub/child-abc`) to its owning
|
|
310
|
+
* node (`hub`). Frame-plane co-location only cares about the physical node, not
|
|
311
|
+
* which forked child within it hosts the session.
|
|
312
|
+
*/
|
|
313
|
+
function collapseNodeId(nodeId) {
|
|
314
|
+
const slash = nodeId.indexOf("/");
|
|
315
|
+
return slash >= 0 ? nodeId.slice(0, slash) : nodeId;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Decide whether a decoder session created on `decoderNodeId` is co-located
|
|
319
|
+
* with the broker whose local node is `brokerNodeId`.
|
|
320
|
+
*
|
|
321
|
+
* The broker's shm frame ring is NODE-LOCAL: a decoder session on any other
|
|
322
|
+
* node can never deliver frame handles to this broker's consumers, so a
|
|
323
|
+
* mis-placed session must be rejected. Both ids are collapsed (`hub/child`→
|
|
324
|
+
* `hub`) before comparison.
|
|
325
|
+
*
|
|
326
|
+
* A `null` `brokerNodeId` means the broker's own node is unknown — enforcement
|
|
327
|
+
* is skipped (legacy behavior, accept any placement).
|
|
328
|
+
*/
|
|
329
|
+
function isDecoderNodeColocated(brokerNodeId, decoderNodeId) {
|
|
330
|
+
if (brokerNodeId === null) return true;
|
|
331
|
+
return collapseNodeId(brokerNodeId) === collapseNodeId(decoderNodeId);
|
|
332
|
+
}
|
|
307
333
|
/** Per-subscription handle-queue capacity. A handle is tiny; 64 absorbs a
|
|
308
334
|
* burst between two `pullFrameHandles` polls without unbounded growth. */
|
|
309
335
|
var HANDLE_QUEUE_CAPACITY = 64;
|
|
@@ -320,6 +346,7 @@ var FrameHandlePlane = class {
|
|
|
320
346
|
decoderApi;
|
|
321
347
|
logger;
|
|
322
348
|
resolveStreamInfo;
|
|
349
|
+
localNodeId;
|
|
323
350
|
subscriptions = /* @__PURE__ */ new Map();
|
|
324
351
|
sessions = /* @__PURE__ */ new Map();
|
|
325
352
|
disposed = false;
|
|
@@ -335,6 +362,7 @@ var FrameHandlePlane = class {
|
|
|
335
362
|
this.decoderApi = options.decoderApi;
|
|
336
363
|
this.logger = options.logger;
|
|
337
364
|
this.resolveStreamInfo = options.resolveStreamInfo;
|
|
365
|
+
this.localNodeId = options.localNodeId;
|
|
338
366
|
}
|
|
339
367
|
/** Number of active handle subscriptions — surfaced for diagnostics. */
|
|
340
368
|
get subscriberCount() {
|
|
@@ -635,6 +663,22 @@ var FrameHandlePlane = class {
|
|
|
635
663
|
...info.numericDeviceId !== void 0 ? { deviceId: info.numericDeviceId } : {},
|
|
636
664
|
tag: `${info.tag}:shm:${format}`
|
|
637
665
|
});
|
|
666
|
+
if (!isDecoderNodeColocated(this.localNodeId, nodeId)) {
|
|
667
|
+
this.logger?.warn("frame-handle plane: decoder session landed on a non-local node — destroying (shm ring is node-local)", { meta: {
|
|
668
|
+
format,
|
|
669
|
+
requestedNode: this.localNodeId,
|
|
670
|
+
gotNode: nodeId,
|
|
671
|
+
sessionId
|
|
672
|
+
} });
|
|
673
|
+
await this.decoderApi.destroySession({ sessionId }).catch((err) => {
|
|
674
|
+
this.logger?.warn("frame-handle plane: failed to destroy mis-placed decoder session", { meta: {
|
|
675
|
+
format,
|
|
676
|
+
sessionId,
|
|
677
|
+
error: errMsg(err)
|
|
678
|
+
} });
|
|
679
|
+
});
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
638
682
|
const proxy = new DecoderSessionProxy(this.decoderApi, sessionId);
|
|
639
683
|
session.proxy = proxy;
|
|
640
684
|
session.nodeId = nodeId;
|
|
@@ -4079,6 +4123,13 @@ var StreamBroker = class StreamBroker {
|
|
|
4079
4123
|
decoderApi;
|
|
4080
4124
|
audioCodecApi;
|
|
4081
4125
|
logger;
|
|
4126
|
+
/**
|
|
4127
|
+
* This broker's OWN local Moleculer node id, threaded from the manager
|
|
4128
|
+
* (`ctx.kernel.localNodeId`). Passed to the `FrameHandlePlane` so it can
|
|
4129
|
+
* reject decoder sessions that land on a different node (the shm frame ring
|
|
4130
|
+
* is node-local). `null` = unknown → co-location enforcement skipped.
|
|
4131
|
+
*/
|
|
4132
|
+
localNodeId;
|
|
4082
4133
|
startedAt = Date.now();
|
|
4083
4134
|
nativeClient = null;
|
|
4084
4135
|
rfc4571Reader = null;
|
|
@@ -4265,11 +4316,12 @@ var StreamBroker = class StreamBroker {
|
|
|
4265
4316
|
static DEFAULT_PRE_BUFFER_SEC = 10;
|
|
4266
4317
|
/** Maximum allowed pre-buffer duration in seconds */
|
|
4267
4318
|
static MAX_PRE_BUFFER_SEC = 30;
|
|
4268
|
-
constructor(deviceId, decoderApi, logger, audioCodecApi) {
|
|
4319
|
+
constructor(deviceId, decoderApi, logger, audioCodecApi, localNodeId) {
|
|
4269
4320
|
this.deviceId = deviceId;
|
|
4270
4321
|
this.decoderApi = decoderApi;
|
|
4271
4322
|
this.audioCodecApi = audioCodecApi ?? null;
|
|
4272
4323
|
this.logger = logger;
|
|
4324
|
+
this.localNodeId = localNodeId ?? null;
|
|
4273
4325
|
this.pipeServer = new StreamPipeServer(logger?.child("pipe-server"));
|
|
4274
4326
|
this.rtspRestreamer = new RtspRestreamer(deviceId);
|
|
4275
4327
|
if (logger) this.rtspRestreamer.setLogger(logger.child("rtsp"));
|
|
@@ -5015,7 +5067,8 @@ var StreamBroker = class StreamBroker {
|
|
|
5015
5067
|
this.frameHandlePlane = new FrameHandlePlane({
|
|
5016
5068
|
decoderApi: this.decoderApi,
|
|
5017
5069
|
logger: this.logger?.child("frame-handle-plane"),
|
|
5018
|
-
resolveStreamInfo: () => this.resolveFrameHandleStreamInfo()
|
|
5070
|
+
resolveStreamInfo: () => this.resolveFrameHandleStreamInfo(),
|
|
5071
|
+
localNodeId: this.localNodeId
|
|
5019
5072
|
});
|
|
5020
5073
|
return this.frameHandlePlane;
|
|
5021
5074
|
}
|
|
@@ -6581,35 +6634,6 @@ var RtspRestreamProviderImpl = class {
|
|
|
6581
6634
|
}
|
|
6582
6635
|
};
|
|
6583
6636
|
//#endregion
|
|
6584
|
-
//#region src/stream-broker/stream-broker/ffmpeg-args-common.ts
|
|
6585
|
-
var AUDIO_ENCODER_BY_CODEC = {
|
|
6586
|
-
opus: "libopus",
|
|
6587
|
-
aac: "aac",
|
|
6588
|
-
pcmu: "pcm_mulaw",
|
|
6589
|
-
pcma: "pcm_alaw",
|
|
6590
|
-
copy: "copy"
|
|
6591
|
-
};
|
|
6592
|
-
/**
|
|
6593
|
-
* Build the audio-encode args `-c:a <enc> [-b:a Nk] [-ar N] [-ac N]`. Each knob
|
|
6594
|
-
* is emitted only when provided, in this fixed order — matching what every
|
|
6595
|
-
* broker site already emitted by hand.
|
|
6596
|
-
*/
|
|
6597
|
-
function audioEncoderArgs(codec, opts = {}) {
|
|
6598
|
-
const args = ["-c:a", AUDIO_ENCODER_BY_CODEC[codec]];
|
|
6599
|
-
if (opts.bitrateKbps !== void 0) args.push("-b:a", `${opts.bitrateKbps}k`);
|
|
6600
|
-
if (opts.sampleRateHz !== void 0) args.push("-ar", String(opts.sampleRateHz));
|
|
6601
|
-
if (opts.channels !== void 0) args.push("-ac", String(opts.channels));
|
|
6602
|
-
return args;
|
|
6603
|
-
}
|
|
6604
|
-
/** The `-hide_banner -loglevel <level>` preamble every broker ffmpeg site opens with. */
|
|
6605
|
-
function logBannerArgs(level) {
|
|
6606
|
-
return [
|
|
6607
|
-
"-hide_banner",
|
|
6608
|
-
"-loglevel",
|
|
6609
|
-
level
|
|
6610
|
-
];
|
|
6611
|
-
}
|
|
6612
|
-
//#endregion
|
|
6613
6637
|
//#region src/stream-broker/stream-broker/ffmpeg-invocation.ts
|
|
6614
6638
|
/** The full set of hwaccel settings (for validation + option surfaces). */
|
|
6615
6639
|
var FFMPEG_HW_ACCEL_SETTINGS = [
|
|
@@ -9639,7 +9663,7 @@ var StreamBrokerManager = class {
|
|
|
9639
9663
|
camStreamId
|
|
9640
9664
|
});
|
|
9641
9665
|
const source = this.buildSourceFromCamStream(cam);
|
|
9642
|
-
const broker = new StreamBroker(brokerId, this.buildDecoderCapApi(deviceId), brokerLogger, buildAudioCodecCapApi(this.api));
|
|
9666
|
+
const broker = new StreamBroker(brokerId, this.buildDecoderCapApi(deviceId), brokerLogger, buildAudioCodecCapApi(this.api), this.localNodeId);
|
|
9643
9667
|
broker.setRestreamers(this.restreamers);
|
|
9644
9668
|
this.applyPreBufferConfig(broker, deviceId, camStreamId, cam);
|
|
9645
9669
|
if (this.deviceOverrides.get(deviceId)?.streamingDebug) broker.setStreamingDebug(true);
|
|
@@ -16863,27 +16887,6 @@ var StreamBrokerAddon = class extends BaseAddon {
|
|
|
16863
16887
|
}
|
|
16864
16888
|
};
|
|
16865
16889
|
//#endregion
|
|
16866
|
-
//#region src/stream-broker/stream-broker/frame-dropper.ts
|
|
16867
|
-
var FrameDropper = class {
|
|
16868
|
-
intervalMs;
|
|
16869
|
-
lastPassedAt = -Infinity;
|
|
16870
|
-
constructor(maxFps) {
|
|
16871
|
-
this.intervalMs = maxFps > 0 ? 1e3 / maxFps : 0;
|
|
16872
|
-
}
|
|
16873
|
-
shouldKeep() {
|
|
16874
|
-
if (this.intervalMs === 0) return true;
|
|
16875
|
-
const now = Date.now();
|
|
16876
|
-
if (now - this.lastPassedAt >= this.intervalMs) {
|
|
16877
|
-
this.lastPassedAt = now;
|
|
16878
|
-
return true;
|
|
16879
|
-
}
|
|
16880
|
-
return false;
|
|
16881
|
-
}
|
|
16882
|
-
setMaxFps(maxFps) {
|
|
16883
|
-
this.intervalMs = maxFps > 0 ? 1e3 / maxFps : 0;
|
|
16884
|
-
}
|
|
16885
|
-
};
|
|
16886
|
-
//#endregion
|
|
16887
16890
|
//#region src/stream-broker/stream-broker/ffmpeg-decoder-session.ts
|
|
16888
16891
|
/** Minimal no-op logger for default parameter */
|
|
16889
16892
|
var noopLogger = {
|
|
@@ -30,7 +30,7 @@ async function d(e) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async function f() {
|
|
33
|
-
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-w2pP0MYO.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{d as e,f as t,g as n,h as r,m as i,p as a}from"./index-
|
|
1
|
+
import{d as e,f as t,g as n,h as r,m as i,p as a}from"./index-B0IQgci0.js";var o=n(t()),s=n(e(),1),c=Math.PI/180;function l(){return typeof window<`u`&&({}.toString.call(window)===`[object Window]`||{}.toString.call(window)===`[object global]`)}var u=typeof global<`u`?global:typeof window<`u`?window:typeof WorkerGlobalScope<`u`?self:{},d={_global:u,version:`10.3.0`,isBrowser:l(),isUnminified:/param/.test(function(e){}.toString()),dblClickWindow:400,getAngle(e){return d.angleDeg?e*c:e},enableTrace:!1,pointerEventsEnabled:!0,autoDrawEnabled:!0,hitOnDragEnabled:!1,capturePointerEventsEnabled:!1,_mouseListenClick:!1,_touchListenClick:!1,_pointerListenClick:!1,_mouseInDblClickWindow:!1,_touchInDblClickWindow:!1,_pointerInDblClickWindow:!1,_mouseDblClickPointerId:null,_touchDblClickPointerId:null,_pointerDblClickPointerId:null,_renderBackend:`web`,legacyTextRendering:!1,pixelRatio:typeof window<`u`&&window.devicePixelRatio||1,dragDistance:3,angleDeg:!0,showWarnings:!0,dragButtons:[0,1],isDragging(){return d.DD.isDragging},isTransforming(){return d.Transformer?.isTransforming()??!1},isDragReady(){return!!d.DD.node},releaseCanvasOnDestroy:!0,document:u.document,_injectGlobal(e){u.Konva!==void 0&&console.error(`Several Konva instances detected. It is not recommended to use multiple Konva instances in the same environment.`),u.Konva=e}},f=e=>{d[e.prototype.getClassName()]=e};d._injectGlobal(d);var p=`Konva.js unsupported environment.
|
|
2
2
|
|
|
3
3
|
Looks like you are trying to use Konva.js in Node.js environment. because "document" object is undefined.
|
|
4
4
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,c as t,d as n,f as r,g as i,l as a,o,r as s,s as c,u as l}from"./index-
|
|
1
|
+
import{a as e,c as t,d as n,f as r,g as i,l as a,o,r as s,s as c,u as l}from"./index-B0IQgci0.js";import{MaskShapeCanvas as u}from"./MaskShapeCanvas-DI4BY7W2-CYmHXacX.js";var d=i(r(),1),f=i(n(),1),p=o(`grid-2x2`,[[`path`,{d:`M12 3v18`,key:`108xh3`}],[`path`,{d:`M3 12h18`,key:`1i2n21`}],[`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`,key:`h1oib`}]]),m=110,h=`motion-zones`,g=0,_=[1,2,3],v=1,y=`rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-medium text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,b=`rounded-md border border-primary/50 bg-primary/15 px-2.5 py-1 text-[11px] font-medium text-primary hover:bg-primary/25 disabled:opacity-40 transition-colors`;function x(e,t,n){let r=t*n,i=Array.from({length:r});for(let t=0;t<r;t+=1)i[t]=e[t]===!0;return i}function S(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1)if(e[n]!==t[n])return!1;return!0}function C(e,t){return Math.ceil(e/t)}function w(e,t,n){return Math.min(n-1,Math.floor((e+.5)/t*n))}function T(e,t,n,r){let i=C(t,r),a=C(n,r),o=Array.from({length:i*a},()=>!1);for(let r=0;r<n;r+=1){let s=w(r,n,a);for(let n=0;n<t;n+=1)e[r*t+n]===!0&&(o[s*i+w(n,t,i)]=!0)}return o}function E(e,t,n,r){let i=C(t,r),a=C(n,r),o=Array.from({length:t*n},()=>!1);for(let r=0;r<n;r+=1){let s=w(r,n,a);for(let n=0;n<t;n+=1)o[r*t+n]=e[s*i+w(n,t,i)]===!0}return o}function D({deviceId:n}){let r=t(l().trpcClient,n),[i,o]=(0,d.useState)(null),[w,D]=(0,d.useState)(!1),[O,k]=(0,d.useState)(null),[A,j]=(0,d.useState)(null),[M,N]=(0,d.useState)(v),[P,F]=(0,d.useState)(!1),[I,L]=(0,d.useState)(!1),R=(0,d.useRef)(!1);(0,d.useEffect)(()=>{if(!r)return;let e=!1;return R.current=!1,o(null),D(!1),k(null),j(null),N(v),L(!1),(async()=>{try{let t=await r.motionZones?.getOptions({});if(e)return;if(!t)throw Error(`device proxy not ready`);if(o(t),R.current)return;let n=await r.motionZones?.getStatus({});if(e)return;if(!n)throw Error(`device proxy not ready`);R.current=!0;let i=n.regions.find(e=>e.shape.kind===`grid`),a=x(i?i.shape.cells:[],t.grid.width,t.grid.height);j(a),k(T(a,t.grid.width,t.grid.height,v))}catch(t){if(e)return;c(t)?D(!0):console.error(`Motion Zones load failed`,t)}})(),()=>{e=!0}},[r]);let z=i?i.grid.width:0,B=i?i.grid.height:0,V=C(z,M),H=C(B,M),U=(0,d.useMemo)(()=>O&&i?E(O,z,B,M):null,[O,i,z,B,M]),W=(0,d.useMemo)(()=>U!==null&&A!==null&&!S(U,A),[U,A]),G=(0,d.useMemo)(()=>U?U.reduce((e,t)=>t?e+1:e,0):0,[U]),K=z*B,q=V*H,J=(0,d.useCallback)(e=>{q>0&&k(Array.from({length:q},()=>e))},[q]),Y=(0,d.useCallback)(()=>{k(e=>e&&e.map(e=>!e))},[]),X=(0,d.useCallback)(()=>{A&&i&&k(T(A,z,B,M))},[A,i,z,B,M]),Z=(0,d.useCallback)(e=>{e===M||!i||k(t=>{if(!t)return N(e),t;let n=T(E(t,z,B,M),z,B,e);return N(e),n})},[M,i,z,B]),Q=(0,d.useMemo)(()=>i&&O?[{id:g,shape:{kind:`grid`,gridWidth:V,gridHeight:H,cells:[...O]}}]:[],[i,O,V,H]),$=(0,d.useCallback)((e,t)=>{t.kind===`grid`&&k(t.cells)},[]),ee=(0,d.useCallback)(async()=>{if(!(!r||!O||!i)){F(!0);try{let e=E(O,i.grid.width,i.grid.height,M),t={kind:`grid`,gridWidth:i.grid.width,gridHeight:i.grid.height,cells:e};await r.motionZones?.setZone({patch:{regions:[{id:g,enabled:!0,shape:t}]}});let n=await r.motionZones?.getStatus({});if(n){let e=n.regions.find(e=>e.shape.kind===`grid`),t=x(e?e.shape.cells:[],i.grid.width,i.grid.height);j(t),k(T(t,i.grid.width,i.grid.height,M))}}catch(e){console.error(`Motion Zones save failed`,e)}finally{F(!1)}}},[r,O,i,M]);a((0,d.useMemo)(()=>I&&!w&&i&&O?{id:h,order:m,node:(0,f.jsx)(u,{transparent:!0,items:Q,supportedShapes:[`grid`],grid:{width:V,height:H},selectedId:g,onSelect:()=>{},onShapeChange:$,onDrawComplete:()=>{},drawingKind:null})}:null,[I,w,i,O,Q,$,V,H]));let te=!w&&i!==null&&O!==null;return r?(0,f.jsx)(e,{title:`Motion Zones`,icon:(0,f.jsx)(p,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,f.jsx)(`div`,{className:`flex flex-col gap-3`,children:w?(0,f.jsx)(`p`,{className:`${s} leading-relaxed`,children:`This camera doesn't expose an on-board motion zones grid.`}):te?(0,f.jsxs)(f.Fragment,{children:[(0,f.jsxs)(`p`,{className:`${s} leading-relaxed`,children:[`Toggle `,(0,f.jsx)(`strong`,{className:`text-foreground`,children:`Edit grid`}),` to paint the region directly on the live frame, then `,(0,f.jsx)(`strong`,{className:`text-foreground`,children:`Save`}),` to push the mask to the camera. Pick a bigger`,` `,(0,f.jsx)(`strong`,{className:`text-foreground`,children:`Cell size`}),` for quicker, broad-stroke painting — it's resampled to the camera's native `,i.grid.width,`×`,i.grid.height,` grid on save (×1 is the finest).`]}),(0,f.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,f.jsx)(`button`,{type:`button`,onClick:()=>L(e=>!e),disabled:P,"aria-pressed":I,className:I?b:y,children:I?`Done editing`:`Edit grid`}),(0,f.jsx)(`button`,{type:`button`,onClick:()=>J(!0),disabled:P,className:y,children:`All on`}),(0,f.jsx)(`button`,{type:`button`,onClick:()=>J(!1),disabled:P,className:y,children:`All off`}),(0,f.jsx)(`button`,{type:`button`,onClick:Y,disabled:P,className:y,children:`Invert`}),(0,f.jsxs)(`div`,{className:`flex items-center gap-1 ml-1`,role:`group`,"aria-label":`Cell size`,children:[(0,f.jsx)(`span`,{className:`${s} mr-0.5`,children:`Cell size`}),_.map(e=>(0,f.jsxs)(`button`,{type:`button`,onClick:()=>Z(e),disabled:P,"aria-pressed":M===e,title:e===1?`Camera grid ${z}×${B} (finest)`:`${C(z,e)}×${C(B,e)} painting grid · cells ×${e} bigger`,className:M===e?b:y,children:[`×`,e]},e))]}),(0,f.jsxs)(`span`,{className:`${s} ml-1 tabular-nums`,children:[G,` / `,K,` cells · `,i.grid.width,`×`,i.grid.height,M===1?``:` · paint ${V}×${H}`]}),(0,f.jsx)(`span`,{className:`flex-1`}),(0,f.jsx)(`button`,{type:`button`,onClick:X,disabled:P||!W,className:`rounded-md border border-border bg-surface px-2 py-1 text-[11px] text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,children:`Revert`}),(0,f.jsx)(`button`,{type:`button`,onClick:()=>void ee(),disabled:P||!W,className:b,children:P?`Saving…`:`Save`})]})]}):(0,f.jsx)(`p`,{className:`${s} leading-relaxed`,children:`Loading the camera's grid…`})})}):null}export{D as MotionZonesSettings};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,c as t,d as n,f as r,g as i,i as a,l as o,n as s,o as c,r as l,s as u,t as d,u as ee}from"./index-
|
|
1
|
+
import{a as e,c as t,d as n,f as r,g as i,i as a,l as o,n as s,o as c,r as l,s as u,t as d,u as ee}from"./index-B0IQgci0.js";import{MaskShapeCanvas as te}from"./MaskShapeCanvas-DI4BY7W2-CYmHXacX.js";var f=i(r(),1),p=i(n(),1),m=c(`hexagon`,[[`path`,{d:`M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z`,key:`yt0hxn`}]]),h=120,g=`privacy-mask`,_=`rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-medium text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,v=`rounded-md border border-primary/50 bg-primary/15 px-2.5 py-1 text-[11px] font-medium text-primary hover:bg-primary/25 disabled:opacity-40 transition-colors`;function y(e){return e.kind===`rect`||e.kind===`polygon`?e:null}function b(e){let t=new Set(e.map(e=>e.id)),n=0;for(;t.has(n);)n+=1;return n}function x(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1)if(JSON.stringify(e[n])!==JSON.stringify(t[n]))return!1;return!0}function S({deviceId:n}){let r=t(ee().trpcClient,n),[i,c]=(0,f.useState)(null),[S,C]=(0,f.useState)(!1),[w,T]=(0,f.useState)(null),[E,D]=(0,f.useState)(null),[O,k]=(0,f.useState)(!1),[A,j]=(0,f.useState)(!1),[M,N]=(0,f.useState)(null),[P,F]=(0,f.useState)(null),I=(0,f.useRef)(!1);(0,f.useEffect)(()=>{if(!r)return;let e=!1;return I.current=!1,c(null),C(!1),T(null),D(null),j(!1),N(null),F(null),(async()=>{try{let t=await r.privacyMask?.getOptions({});if(e)return;if(!t)throw Error(`device proxy not ready`);if(c(t),I.current)return;let n=await r.privacyMask?.getStatus({});if(e)return;if(!n)throw Error(`device proxy not ready`);I.current=!0;let i={enabled:n.enabled,regions:n.regions};D(i),T(i)}catch(t){if(e)return;u(t)?C(!0):console.error(`Privacy Mask load failed`,t)}})(),()=>{e=!0}},[r]);let L=(0,f.useMemo)(()=>w!==null&&E!==null&&(w.enabled!==E.enabled||!x(w.regions,E.regions)),[w,E]),R=w?w.regions.length:0,z=i?i.maxRegions:0,B=(0,f.useRef)(0);(0,f.useEffect)(()=>{B.current=z},[z]);let V=z>0&&R>=z,H=(0,f.useCallback)(()=>{T(e=>e&&{...e,enabled:!e.enabled})},[]),U=(0,f.useCallback)(e=>{N(typeof e==`number`?e:null)},[]),W=(0,f.useMemo)(()=>w?w.regions.map(e=>({id:e.id,shape:e.shape,enabled:e.enabled,label:`Zone ${String(e.id)}`})):[],[w]),G=(0,f.useCallback)((e,t)=>{let n=y(t);n&&T(t=>t&&{...t,regions:t.regions.map(t=>t.id===e?{...t,shape:n}:t)})},[]),K=(0,f.useCallback)(e=>{let t=y(e);t&&(F(null),T(e=>{if(!e)return e;let n=B.current;if(n>0&&e.regions.length>=n)return e;let r=b(e.regions),i={id:r,enabled:!0,shape:t};return N(r),{...e,regions:[...e.regions,i]}}))},[]),q=(0,f.useCallback)(e=>{j(!0),N(null),F(e)},[]),J=(0,f.useCallback)(e=>{j(!0),F(null),N(e)},[]),Y=(0,f.useCallback)(e=>{N(t=>t===e?null:t),T(t=>t&&{...t,regions:t.regions.filter(t=>t.id!==e)})},[]),X=(0,f.useCallback)(()=>{F(null),N(null),E&&T({enabled:E.enabled,regions:E.regions})},[E]),Z=(0,f.useCallback)(async()=>{if(!(!r||!w)){k(!0);try{await r.privacyMask?.setMask({patch:{enabled:w.enabled,regions:[...w.regions]}});let e=await r.privacyMask?.getStatus({});if(e){let t={enabled:e.enabled,regions:e.regions};D(t),T(t)}}catch(e){console.error(`Privacy Mask save failed`,e)}finally{k(!1)}}},[r,w]),ne=(0,f.useCallback)(()=>{j(e=>(e&&(F(null),N(null)),!e))},[]),Q=i?.supportedShapes??[];o((0,f.useMemo)(()=>A&&!S&&i&&w?{id:g,order:h,node:(0,p.jsx)(te,{transparent:!0,items:W,supportedShapes:Q,polygonVertices:i.polygonVertices,selectedId:M,onSelect:U,onShapeChange:G,onDrawComplete:K,drawingKind:P})}:null,[A,S,i,w,W,Q,M,U,G,K,P]));let re=i?.supportedShapes.includes(`rect`)??!1,ie=i?.supportedShapes.includes(`polygon`)??!1,$=i!==null&&(i.maxRegions<=0||i.supportedShapes.length===0),ae=!S&&!$&&i!==null&&w!==null;return r?(0,p.jsx)(e,{title:`Privacy Mask`,icon:(0,p.jsx)(d,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,p.jsx)(`div`,{className:`flex flex-col gap-3`,children:S||$?(0,p.jsx)(`p`,{className:`${l} leading-relaxed`,children:`This camera doesn't support an on-board privacy mask.`}):ae?(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)(`p`,{className:`${l} leading-relaxed`,children:[`Toggle `,(0,p.jsx)(`strong`,{className:`text-foreground`,children:`Edit mask`}),` to draw blanked-out zones on the live frame, then `,(0,p.jsx)(`strong`,{className:`text-foreground`,children:`Save`}),` to push them to the camera. Drag a rectangle to move, its corner to resize; drag polygon vertices, click an edge midpoint to add one, or right-click a vertex to remove it.`]}),(0,p.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,p.jsx)(`button`,{type:`button`,onClick:ne,disabled:O,"aria-pressed":A,className:A?v:_,children:A?`Done editing`:`Edit mask`}),(0,p.jsx)(`button`,{type:`button`,onClick:H,disabled:O,"aria-pressed":w.enabled,className:w.enabled?v:_,children:w.enabled?`Mask on`:`Mask off`}),re&&(0,p.jsx)(`button`,{type:`button`,onClick:()=>q(`rect`),disabled:O||V,"aria-pressed":P===`rect`,className:P===`rect`?v:_,title:V?`Maximum zones reached`:`Add a rectangle zone`,children:`+ Rect`}),ie&&(0,p.jsx)(`button`,{type:`button`,onClick:()=>q(`polygon`),disabled:O||V,"aria-pressed":P===`polygon`,className:P===`polygon`?v:_,title:V?`Maximum zones reached`:`Add a polygon zone`,children:`+ Polygon`}),(0,p.jsxs)(`span`,{className:`${l} ml-1 tabular-nums`,children:[R,` / `,z,` zones`]}),(0,p.jsx)(`span`,{className:`flex-1`}),(0,p.jsx)(`button`,{type:`button`,onClick:X,disabled:O||!L,className:`rounded-md border border-border bg-surface px-2 py-1 text-[11px] text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,children:`Revert`}),(0,p.jsx)(`button`,{type:`button`,onClick:()=>void Z(),disabled:O||!L,className:v,children:O?`Saving…`:`Save`})]}),R>0?(0,p.jsx)(`div`,{className:`flex flex-col gap-1`,children:w.regions.map(e=>{let t=M===e.id,n=e.shape.kind===`polygon`?m:s;return(0,p.jsxs)(`div`,{className:`flex items-center gap-2 rounded-md border px-2 py-1 transition-colors ${t?`border-primary/50 bg-primary/10`:`border-border bg-surface`}`,children:[(0,p.jsxs)(`button`,{type:`button`,onClick:()=>J(e.id),disabled:O,className:`flex flex-1 items-center gap-2 text-left text-[11px] font-medium text-foreground-subtle hover:text-foreground disabled:opacity-40 transition-colors`,children:[(0,p.jsx)(n,{className:`h-3.5 w-3.5 shrink-0`}),(0,p.jsxs)(`span`,{children:[`Zone `,e.id]}),(0,p.jsx)(`span`,{className:`text-foreground-faint capitalize`,children:e.shape.kind})]}),(0,p.jsx)(`button`,{type:`button`,onClick:()=>Y(e.id),disabled:O,"aria-label":`Delete zone ${String(e.id)}`,title:`Delete zone`,className:`inline-flex h-6 w-6 items-center justify-center rounded border border-border bg-surface text-foreground-subtle hover:border-red-400/40 hover:bg-red-500/10 hover:text-red-400 disabled:opacity-40 transition-colors`,children:(0,p.jsx)(a,{className:`h-3.5 w-3.5`})})]},e.id)})}):null]}):(0,p.jsx)(`p`,{className:`${l} leading-relaxed`,children:`Loading the camera's privacy mask…`})})}):null}export{S as PrivacyMaskSettings};
|