@camstack/types 1.2.43 → 1.2.45
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/addon.js +8 -2
- package/dist/addon.mjs +8 -2
- package/dist/capabilities/index.d.ts +21 -3
- package/dist/capabilities/osd-manager.cap.d.ts +900 -0
- package/dist/capabilities/pipeline-analytics.cap.d.ts +1192 -74
- package/dist/capabilities/pipeline-orchestrator.cap.d.ts +10 -0
- package/dist/capabilities/pipeline-runner.cap.d.ts +6 -0
- package/dist/capabilities/schemas/streaming-shared.d.ts +2 -0
- package/dist/capabilities/server-management.cap.d.ts +3 -3
- package/dist/capabilities/stream-broker.cap.d.ts +32 -0
- package/dist/ffmpeg/fmp4-box-splitter.d.ts +113 -0
- package/dist/ffmpeg/fmp4-fragment-child.d.ts +85 -0
- package/dist/ffmpeg/fmp4-fragment-plane.d.ts +142 -0
- package/dist/ffmpeg/invocation.d.ts +4 -2
- package/dist/fmp4-box-splitter-B53u9-Nu.mjs +615 -0
- package/dist/fmp4-box-splitter-BkWH7O3L.js +686 -0
- package/dist/generated/addon-api.d.ts +162 -0
- package/dist/generated/cap-input-defaults.d.ts +1 -1
- package/dist/generated/capability-router-map.d.ts +5 -2
- package/dist/generated/device-proxy.d.ts +3 -1
- package/dist/generated/method-access-map.d.ts +1 -1
- package/dist/generated/system-proxy.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1114 -407
- package/dist/index.mjs +1075 -397
- package/dist/interfaces/camera-switches.d.ts +48 -0
- package/dist/interfaces/stream-broker.d.ts +19 -49
- package/dist/node.d.ts +4 -0
- package/dist/node.js +509 -3
- package/dist/node.mjs +507 -3
- package/dist/notification/schedule.d.ts +20 -0
- package/dist/{sleep-Bx9IIoT0.js → sleep-BOI-sVEA.js} +37 -1
- package/dist/{sleep-DtstvzWm.mjs → sleep-Bf5fBs7u.mjs} +37 -1
- package/dist/types/detection.d.ts +31 -0
- package/dist/types/pipeline-step.d.ts +22 -1
- package/package.json +1 -1
- package/dist/canonical-hash-7nfBbEqR.mjs +0 -35
- package/dist/canonical-hash-BcZHRHIx.js +0 -40
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_event_category = require("./event-category-BE4PDZ_3.js");
|
|
3
|
-
const require_sleep = require("./sleep-
|
|
4
|
-
const
|
|
3
|
+
const require_sleep = require("./sleep-BOI-sVEA.js");
|
|
4
|
+
const require_fmp4_box_splitter = require("./fmp4-box-splitter-BkWH7O3L.js");
|
|
5
5
|
const require_enums = require("./enums.js");
|
|
6
6
|
const require_err_msg = require("./err-msg-COpsHMw2.js");
|
|
7
7
|
let zod = require("zod");
|
|
@@ -231,383 +231,6 @@ function encodeProfileFromStreamShape(stream) {
|
|
|
231
231
|
};
|
|
232
232
|
}
|
|
233
233
|
//#endregion
|
|
234
|
-
//#region src/ffmpeg/invocation.ts
|
|
235
|
-
var AUDIO_ENCODER_BY_CODEC = {
|
|
236
|
-
opus: "libopus",
|
|
237
|
-
aac: "aac",
|
|
238
|
-
pcmu: "pcm_mulaw",
|
|
239
|
-
pcma: "pcm_alaw"
|
|
240
|
-
};
|
|
241
|
-
/**
|
|
242
|
-
* Camera-microphone audio, per codec. Lives HERE rather than in
|
|
243
|
-
* `encode-defaults.ts` only to avoid an import cycle (`encode-defaults` depends
|
|
244
|
-
* on these types); it is re-exported from there, which is where to read it.
|
|
245
|
-
*
|
|
246
|
-
* Every source in this repo is a mono camera mic. The former broker preset
|
|
247
|
-
* encoded Opus at `channels: 2`, spending bitrate duplicating one channel —
|
|
248
|
-
* that is the value this consolidation changed.
|
|
249
|
-
*/
|
|
250
|
-
var AUDIO_PRESETS = {
|
|
251
|
-
aac: {
|
|
252
|
-
kind: "encode",
|
|
253
|
-
codec: "aac",
|
|
254
|
-
bitrateKbps: 128,
|
|
255
|
-
sampleRateHz: 48e3,
|
|
256
|
-
channels: 1
|
|
257
|
-
},
|
|
258
|
-
opus: {
|
|
259
|
-
kind: "encode",
|
|
260
|
-
codec: "opus",
|
|
261
|
-
bitrateKbps: 64,
|
|
262
|
-
sampleRateHz: 48e3,
|
|
263
|
-
channels: 1
|
|
264
|
-
},
|
|
265
|
-
pcmu: {
|
|
266
|
-
kind: "encode",
|
|
267
|
-
codec: "pcmu",
|
|
268
|
-
sampleRateHz: 8e3,
|
|
269
|
-
channels: 1
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
/** `-hide_banner -loglevel <level>` — every ffmpeg site opens with this. */
|
|
273
|
-
function logBannerArgs(level) {
|
|
274
|
-
return [
|
|
275
|
-
"-hide_banner",
|
|
276
|
-
"-loglevel",
|
|
277
|
-
level
|
|
278
|
-
];
|
|
279
|
-
}
|
|
280
|
-
/** `true` when the resolved value means "decode in software" (⇒ no `-hwaccel`). */
|
|
281
|
-
function isSoftwareDecode(decodeHwAccel) {
|
|
282
|
-
return !decodeHwAccel || decodeHwAccel === "none" || decodeHwAccel === "copy";
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* Every INPUT option, in order, terminated by `-i <url>`. Nothing may be
|
|
286
|
-
* appended to this list by a caller — that is the whole point of the function.
|
|
287
|
-
*/
|
|
288
|
-
function buildInputArgs(input, decodeHwAccel) {
|
|
289
|
-
const args = [];
|
|
290
|
-
if (!isSoftwareDecode(decodeHwAccel)) args.push("-hwaccel", String(decodeHwAccel));
|
|
291
|
-
if (input.extraArgs?.length) args.push(...input.extraArgs);
|
|
292
|
-
if (input.analyzeDurationUs !== void 0) args.push("-analyzeduration", String(input.analyzeDurationUs));
|
|
293
|
-
if (input.probeSizeBytes !== void 0) args.push("-probesize", String(input.probeSizeBytes));
|
|
294
|
-
if (input.fflags?.length) for (const flag of input.fflags) args.push("-fflags", flag);
|
|
295
|
-
if (input.rtspTransport) args.push("-rtsp_transport", input.rtspTransport);
|
|
296
|
-
args.push("-i", input.url);
|
|
297
|
-
return args;
|
|
298
|
-
}
|
|
299
|
-
/** The `-vf` filter args, or `[]` when a consumer `-vf` already claims the slot. */
|
|
300
|
-
function buildVideoFilterArgs(scale, outputArgs) {
|
|
301
|
-
if (!scale) return [];
|
|
302
|
-
if (outputArgs.some((a) => a === "-vf")) return [];
|
|
303
|
-
if (scale.mode === "exact") return ["-vf", `scale=${scale.width}:${scale.height}`];
|
|
304
|
-
return ["-vf", `scale='min(${scale.width},iw)':'min(${scale.height},ih)':force_original_aspect_ratio=decrease:force_divisible_by=2`];
|
|
305
|
-
}
|
|
306
|
-
/** Rate-control args for an encode plan. */
|
|
307
|
-
function buildRateControlArgs(video) {
|
|
308
|
-
const kbps = video.bitrateKbps;
|
|
309
|
-
if (kbps === void 0) return [];
|
|
310
|
-
const rc = video.rateControl ?? {
|
|
311
|
-
kind: "cap",
|
|
312
|
-
vbvSeconds: 2
|
|
313
|
-
};
|
|
314
|
-
const bufsize = Math.max(1, Math.round(kbps * rc.vbvSeconds));
|
|
315
|
-
return [
|
|
316
|
-
...rc.kind === "cbr" ? ["-b:v", `${kbps}k`] : [],
|
|
317
|
-
"-maxrate",
|
|
318
|
-
`${kbps}k`,
|
|
319
|
-
"-bufsize",
|
|
320
|
-
`${bufsize}k`
|
|
321
|
-
];
|
|
322
|
-
}
|
|
323
|
-
/** The whole video block (`-vf` … `-c:v` … knobs), after `-i`. */
|
|
324
|
-
function buildVideoArgs(video, outputArgs) {
|
|
325
|
-
if (video.kind === "copy") return [
|
|
326
|
-
"-c:v",
|
|
327
|
-
"copy",
|
|
328
|
-
...video.bitstreamFilter ? ["-bsf:v", video.bitstreamFilter] : []
|
|
329
|
-
];
|
|
330
|
-
const args = [
|
|
331
|
-
...buildVideoFilterArgs(video.scale, outputArgs),
|
|
332
|
-
"-c:v",
|
|
333
|
-
video.encoder
|
|
334
|
-
];
|
|
335
|
-
if (video.preset !== void 0) args.push("-preset", video.preset);
|
|
336
|
-
if (video.tune !== void 0) args.push("-tune", video.tune);
|
|
337
|
-
if (video.profile !== void 0) args.push("-profile:v", video.profile);
|
|
338
|
-
if (video.level !== void 0) args.push("-level", video.level);
|
|
339
|
-
if (video.pixelFormat !== void 0) args.push("-pix_fmt", video.pixelFormat);
|
|
340
|
-
if (video.fps !== void 0) args.push("-r", String(video.fps));
|
|
341
|
-
if (video.gopFrames !== void 0) args.push("-g", String(video.gopFrames));
|
|
342
|
-
if (video.forceKeyFramesSeconds !== void 0) args.push("-force_key_frames", `expr:gte(t,n_forced*${video.forceKeyFramesSeconds})`);
|
|
343
|
-
if (video.bf !== void 0) args.push("-bf", String(video.bf));
|
|
344
|
-
args.push(...buildRateControlArgs(video));
|
|
345
|
-
if (video.bitstreamFilter !== void 0) args.push("-bsf:v", video.bitstreamFilter);
|
|
346
|
-
return args;
|
|
347
|
-
}
|
|
348
|
-
/** The whole audio block, after `-i`. */
|
|
349
|
-
function buildAudioArgs(audio) {
|
|
350
|
-
if (audio.kind === "none") return ["-an"];
|
|
351
|
-
if (audio.kind === "copy") return ["-c:a", "copy"];
|
|
352
|
-
const args = [];
|
|
353
|
-
if (audio.filter !== void 0) args.push("-af", audio.filter);
|
|
354
|
-
args.push("-c:a", AUDIO_ENCODER_BY_CODEC[audio.codec]);
|
|
355
|
-
if (audio.application !== void 0) args.push("-application", audio.application);
|
|
356
|
-
if (audio.frameDurationMs !== void 0) args.push("-frame_duration", String(audio.frameDurationMs));
|
|
357
|
-
if (audio.globalHeader === true) args.push("-flags", "+global_header");
|
|
358
|
-
if (audio.sampleRateHz !== void 0) args.push("-ar", String(audio.sampleRateHz));
|
|
359
|
-
if (audio.bitrateKbps !== void 0) args.push("-b:a", `${audio.bitrateKbps}k`);
|
|
360
|
-
if (audio.vbvBufferKbits !== void 0) args.push("-bufsize", `${audio.vbvBufferKbits}k`);
|
|
361
|
-
if (audio.channels !== void 0) args.push("-ac", String(audio.channels));
|
|
362
|
-
return args;
|
|
363
|
-
}
|
|
364
|
-
/** RTP output-leg args (`-payload_type`, `-ssrc`, `-sdp_file`, `-f rtp <url>`). */
|
|
365
|
-
function buildRtpOutputArgs(out) {
|
|
366
|
-
const args = [];
|
|
367
|
-
if (out.payloadType !== void 0) args.push("-payload_type", String(out.payloadType));
|
|
368
|
-
if (out.ssrc !== void 0) args.push("-ssrc", String(out.ssrc));
|
|
369
|
-
if (out.sdpFile !== void 0) args.push("-sdp_file", out.sdpFile);
|
|
370
|
-
args.push("-f", "rtp", out.url);
|
|
371
|
-
return args;
|
|
372
|
-
}
|
|
373
|
-
/** `true` when the sink is a raw elementary bytestream that cannot mux audio. */
|
|
374
|
-
function isElementaryVideoSink(sink) {
|
|
375
|
-
return sink.kind === "stdout" && (sink.container === "h264" || sink.container === "hevc");
|
|
376
|
-
}
|
|
377
|
-
/**
|
|
378
|
-
* The fragmented-MP4 muxer flags, in the order the recorder has proven them
|
|
379
|
-
* (`recorder/addon/ffmpeg-args.ts` passes the same `movflags` string through
|
|
380
|
-
* `-segment_format_options`, across every vendor in the fleet):
|
|
381
|
-
*
|
|
382
|
-
* - `frag_keyframe` — cut a fragment at each key frame, so every fragment
|
|
383
|
-
* opens on a sync sample. HKSV's whole requirement.
|
|
384
|
-
* - `empty_moov` — write `ftyp`+`moov` up front with no samples in it, which
|
|
385
|
-
* is what makes the head a standalone INITIALISATION segment.
|
|
386
|
-
* - `default_base_moof` — fragment offsets are self-relative, so a fragment is
|
|
387
|
-
* demuxable without the bytes that preceded it. D31's byte-range read path
|
|
388
|
-
* depends on exactly this property of the recorder's segments.
|
|
389
|
-
*/
|
|
390
|
-
var FMP4_MOVFLAGS = "+frag_keyframe+empty_moov+default_base_moof";
|
|
391
|
-
/**
|
|
392
|
-
* The terminal sink args for every non-`rtp-outputs` sink. Exhaustive over the
|
|
393
|
-
* union so a new member cannot fall through to `['-f', container, 'pipe:1']`,
|
|
394
|
-
* which is what a plain `container` read would have done for `mp4` — a valid
|
|
395
|
-
* argv that writes a NON-fragmented, unseekable-to-a-pipe MP4 and produces one
|
|
396
|
-
* unusable byte stream.
|
|
397
|
-
*/
|
|
398
|
-
function buildStdoutOrRtspSinkArgs(sink) {
|
|
399
|
-
if (sink.kind === "rtsp-listen") return [
|
|
400
|
-
"-f",
|
|
401
|
-
"rtsp",
|
|
402
|
-
"-rtsp_transport",
|
|
403
|
-
"tcp",
|
|
404
|
-
"-rtsp_flags",
|
|
405
|
-
"listen",
|
|
406
|
-
sink.url
|
|
407
|
-
];
|
|
408
|
-
if (sink.kind === "rtp-outputs") return [];
|
|
409
|
-
return sink.container === "mp4" ? buildFmp4SinkArgs(sink) : [
|
|
410
|
-
"-f",
|
|
411
|
-
sink.container,
|
|
412
|
-
"pipe:1"
|
|
413
|
-
];
|
|
414
|
-
}
|
|
415
|
-
/** `-movflags … -min_frag_duration <us> -f mp4 pipe:1`. */
|
|
416
|
-
function buildFmp4SinkArgs(sink) {
|
|
417
|
-
return [
|
|
418
|
-
"-movflags",
|
|
419
|
-
FMP4_MOVFLAGS,
|
|
420
|
-
"-min_frag_duration",
|
|
421
|
-
String(Math.max(0, Math.round(sink.fragmentMs * 1e3))),
|
|
422
|
-
"-f",
|
|
423
|
-
"mp4",
|
|
424
|
-
"pipe:1"
|
|
425
|
-
];
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* A second output mapping source audio to RTP-over-UDP. `0:a:0?` makes the
|
|
429
|
-
* audio optional so a source with no audio skips it instead of failing the
|
|
430
|
-
* whole invocation.
|
|
431
|
-
*/
|
|
432
|
-
function buildAudioSidecarArgs(sidecar) {
|
|
433
|
-
return [
|
|
434
|
-
"-map",
|
|
435
|
-
"0:a:0?",
|
|
436
|
-
...buildAudioArgs(sidecar.codec === "pcma" ? {
|
|
437
|
-
kind: "encode",
|
|
438
|
-
codec: "pcma",
|
|
439
|
-
sampleRateHz: 8e3,
|
|
440
|
-
channels: 1
|
|
441
|
-
} : AUDIO_PRESETS[sidecar.codec]),
|
|
442
|
-
...buildRtpOutputArgs({
|
|
443
|
-
url: sidecar.rtpUrl,
|
|
444
|
-
sdpFile: sidecar.sdpFile
|
|
445
|
-
})
|
|
446
|
-
];
|
|
447
|
-
}
|
|
448
|
-
/**
|
|
449
|
-
* Assemble the full ffmpeg argument list. Layout:
|
|
450
|
-
*
|
|
451
|
-
* -hide_banner -loglevel <level>
|
|
452
|
-
* [-hwaccel <backend|auto>] ─┐ INPUT options — strictly before -i.
|
|
453
|
-
* [<input.extraArgs>] │
|
|
454
|
-
* [-fflags <flag>…] │
|
|
455
|
-
* [-rtsp_transport tcp] │
|
|
456
|
-
* -i <url> ─┘
|
|
457
|
-
* <video block> <threads> <audio block> ─┐ OUTPUT options.
|
|
458
|
-
* <consumer outputArgs verbatim> │
|
|
459
|
-
* <sink> ─┘ terminal
|
|
460
|
-
*/
|
|
461
|
-
function buildFfmpegArgs(inv) {
|
|
462
|
-
const head = [...logBannerArgs(inv.logLevel), ...buildInputArgs(inv.input, inv.decodeHwAccel)];
|
|
463
|
-
const threadArgs = inv.threadCount > 0 ? ["-threads", String(inv.threadCount)] : [];
|
|
464
|
-
if (inv.sink.kind === "rtp-outputs") {
|
|
465
|
-
const videoLeg = inv.sink.video ? [
|
|
466
|
-
"-an",
|
|
467
|
-
"-map",
|
|
468
|
-
"0:v:0",
|
|
469
|
-
...buildVideoArgs(inv.video, inv.outputArgs),
|
|
470
|
-
...threadArgs,
|
|
471
|
-
...inv.outputArgs,
|
|
472
|
-
...buildRtpOutputArgs(inv.sink.video)
|
|
473
|
-
] : [];
|
|
474
|
-
const audioLeg = inv.sink.audio ? [
|
|
475
|
-
"-vn",
|
|
476
|
-
"-map",
|
|
477
|
-
"0:a:0?",
|
|
478
|
-
...buildAudioArgs(inv.audio),
|
|
479
|
-
...buildRtpOutputArgs(inv.sink.audio)
|
|
480
|
-
] : [];
|
|
481
|
-
return [
|
|
482
|
-
...head,
|
|
483
|
-
...videoLeg,
|
|
484
|
-
...audioLeg
|
|
485
|
-
];
|
|
486
|
-
}
|
|
487
|
-
const audioArgs = isElementaryVideoSink(inv.sink) ? ["-an"] : buildAudioArgs(inv.audio);
|
|
488
|
-
const sinkArgs = buildStdoutOrRtspSinkArgs(inv.sink);
|
|
489
|
-
return [
|
|
490
|
-
...head,
|
|
491
|
-
...buildVideoArgs(inv.video, inv.outputArgs),
|
|
492
|
-
...threadArgs,
|
|
493
|
-
...audioArgs,
|
|
494
|
-
...inv.outputArgs,
|
|
495
|
-
...sinkArgs,
|
|
496
|
-
...inv.audioSidecar ? buildAudioSidecarArgs(inv.audioSidecar) : []
|
|
497
|
-
];
|
|
498
|
-
}
|
|
499
|
-
/**
|
|
500
|
-
* Hardware ENCODER ids per decode-hwaccel backend — a static, deterministic
|
|
501
|
-
* map (the same shape the reference NVR uses: platform → encoder, no probe).
|
|
502
|
-
*/
|
|
503
|
-
var ENCODER_IDS_BY_BACKEND = {
|
|
504
|
-
videotoolbox: {
|
|
505
|
-
h264: "h264_videotoolbox",
|
|
506
|
-
h265: "hevc_videotoolbox"
|
|
507
|
-
},
|
|
508
|
-
vaapi: {
|
|
509
|
-
h264: "h264_vaapi",
|
|
510
|
-
h265: "hevc_vaapi"
|
|
511
|
-
},
|
|
512
|
-
qsv: {
|
|
513
|
-
h264: "h264_qsv",
|
|
514
|
-
h265: "hevc_qsv"
|
|
515
|
-
},
|
|
516
|
-
cuda: {
|
|
517
|
-
h264: "h264_nvenc",
|
|
518
|
-
h265: "hevc_nvenc"
|
|
519
|
-
},
|
|
520
|
-
nvdec: {
|
|
521
|
-
h264: "h264_nvenc",
|
|
522
|
-
h265: "hevc_nvenc"
|
|
523
|
-
},
|
|
524
|
-
amf: {
|
|
525
|
-
h264: "h264_amf",
|
|
526
|
-
h265: "hevc_amf"
|
|
527
|
-
}
|
|
528
|
-
};
|
|
529
|
-
/**
|
|
530
|
-
* The hardware encoder for a target codec on `backend`, or the software one.
|
|
531
|
-
* `'auto'` is NOT a backend identity (it is an instruction to ffmpeg), so it
|
|
532
|
-
* maps to software encoding.
|
|
533
|
-
*/
|
|
534
|
-
function pickVideoEncoder(target, backend, useHardware) {
|
|
535
|
-
const software = target === "h264" ? "libx264" : "libx265";
|
|
536
|
-
if (!useHardware || backend === null || isSoftwareDecode(backend) || backend === "auto") return software;
|
|
537
|
-
const ids = ENCODER_IDS_BY_BACKEND[backend.toLowerCase()];
|
|
538
|
-
if (!ids) return software;
|
|
539
|
-
return target === "h264" ? ids.h264 : ids.h265;
|
|
540
|
-
}
|
|
541
|
-
/** Map an `EncodeProfile.audio` to an audio plan. */
|
|
542
|
-
function audioPlanFromEncodeProfile(audio) {
|
|
543
|
-
if (audio === "passthrough") return { kind: "none" };
|
|
544
|
-
if (audio.codec === "copy") return { kind: "copy" };
|
|
545
|
-
return {
|
|
546
|
-
kind: "encode",
|
|
547
|
-
codec: audio.codec,
|
|
548
|
-
...audio.bitrateKbps !== void 0 ? { bitrateKbps: audio.bitrateKbps } : {},
|
|
549
|
-
...audio.sampleRateHz !== void 0 ? { sampleRateHz: audio.sampleRateHz } : {},
|
|
550
|
-
...audio.channels !== void 0 ? { channels: audio.channels } : {}
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
/**
|
|
554
|
-
* Adapt an `EncodeProfile` (the operator/consumer-facing shape) into an
|
|
555
|
-
* {@link FfmpegInvocation}. This is the ONLY bridge between the two models —
|
|
556
|
-
* a second one is how the repo grew two argv builders that disagreed about
|
|
557
|
-
* hardware.
|
|
558
|
-
*
|
|
559
|
-
* Smart video copy: when the source already speaks the requested codec the
|
|
560
|
-
* encode block is elided entirely and ffmpeg runs as a re-muxer on the video
|
|
561
|
-
* plane. Width / height / fps / bitrate in the profile are a downstream BUDGET,
|
|
562
|
-
* not a forced rescale.
|
|
563
|
-
*/
|
|
564
|
-
function invocationFromEncodeProfile(input) {
|
|
565
|
-
const v = input.profile.video;
|
|
566
|
-
const shouldCopy = v.codec === "copy" || input.forceReencode !== true && v.codec === input.sourceCodec;
|
|
567
|
-
const scale = v.width !== void 0 && v.height !== void 0 ? {
|
|
568
|
-
mode: "fit",
|
|
569
|
-
width: v.width,
|
|
570
|
-
height: v.height
|
|
571
|
-
} : null;
|
|
572
|
-
const target = v.codec === "h265" ? "h265" : "h264";
|
|
573
|
-
const video = shouldCopy ? {
|
|
574
|
-
kind: "copy",
|
|
575
|
-
...input.bitstreamFilter !== void 0 ? { bitstreamFilter: input.bitstreamFilter } : {}
|
|
576
|
-
} : {
|
|
577
|
-
kind: "encode",
|
|
578
|
-
encoder: pickVideoEncoder(target, input.decodeHwAccel, input.hardwareEncoders === true),
|
|
579
|
-
scale,
|
|
580
|
-
...v.preset !== void 0 ? { preset: v.preset } : {},
|
|
581
|
-
...v.tune !== void 0 ? { tune: v.tune } : {},
|
|
582
|
-
...v.profile !== void 0 ? { profile: v.profile } : {},
|
|
583
|
-
...v.level !== void 0 ? { level: v.level } : {},
|
|
584
|
-
...input.pixelFormat !== void 0 ? { pixelFormat: input.pixelFormat } : {},
|
|
585
|
-
...v.fps !== void 0 ? { fps: v.fps } : {},
|
|
586
|
-
...v.gopFrames !== void 0 ? { gopFrames: v.gopFrames } : {},
|
|
587
|
-
...input.forceKeyFramesSeconds !== void 0 ? { forceKeyFramesSeconds: input.forceKeyFramesSeconds } : {},
|
|
588
|
-
...v.bf !== void 0 ? { bf: v.bf } : {},
|
|
589
|
-
...v.bitrateKbps !== void 0 ? { bitrateKbps: v.bitrateKbps } : {},
|
|
590
|
-
...input.rateControl !== void 0 ? { rateControl: input.rateControl } : {},
|
|
591
|
-
...input.bitstreamFilter !== void 0 ? { bitstreamFilter: input.bitstreamFilter } : {}
|
|
592
|
-
};
|
|
593
|
-
return {
|
|
594
|
-
logLevel: input.logLevel ?? "error",
|
|
595
|
-
decodeHwAccel: input.decodeHwAccel,
|
|
596
|
-
input: {
|
|
597
|
-
url: input.sourceUrl,
|
|
598
|
-
rtspTransport: "tcp",
|
|
599
|
-
fflags: ["+discardcorrupt"],
|
|
600
|
-
...input.profile.inputArgs?.length ? { extraArgs: input.profile.inputArgs } : {}
|
|
601
|
-
},
|
|
602
|
-
video,
|
|
603
|
-
audio: audioPlanFromEncodeProfile(input.profile.audio),
|
|
604
|
-
threadCount: input.threadCount ?? 0,
|
|
605
|
-
outputArgs: input.profile.outputArgs ?? [],
|
|
606
|
-
sink: input.sink,
|
|
607
|
-
...input.audioSidecar !== void 0 ? { audioSidecar: input.audioSidecar } : {}
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
//#endregion
|
|
611
234
|
//#region src/ffmpeg/encode-defaults.ts
|
|
612
235
|
/**
|
|
613
236
|
* The shape every live egress starts from: H.264 Baseline 3.1 at 720p25.
|
|
@@ -858,7 +481,7 @@ function canonicalEgressPlan(request, delivery = egressTransportFromRequest(requ
|
|
|
858
481
|
* every other consumer to 360p.
|
|
859
482
|
*/
|
|
860
483
|
function egressTranscodeSharingKey(request, delivery = egressTransportFromRequest(request)) {
|
|
861
|
-
return `egress:${
|
|
484
|
+
return `egress:${require_fmp4_box_splitter.canonicalHash(canonicalEgressPlan(request, delivery))}`;
|
|
862
485
|
}
|
|
863
486
|
//#endregion
|
|
864
487
|
//#region src/health/wiring-health.ts
|
|
@@ -991,6 +614,19 @@ var DEFAULT_RETENTION = {
|
|
|
991
614
|
* | `notifications` | `notificationRules.setDeviceMuted` | `NotificationCenter.evaluateAndEnqueue` returns before any rule is evaluated |
|
|
992
615
|
* | `privacy-mask` | `privacyMask.setMask({ enabled })` → the CAMERA | the camera blanks the masked regions itself; every stream and recording carries the black boxes |
|
|
993
616
|
* | `device-audio` | `privacyMask.setAudioEnabled` → the CAMERA | the camera stops encoding an audio track at all; every consumer sees silent video |
|
|
617
|
+
* | `broker-audio` | `streamBroker.setDeviceAudioMute` → `DeviceOverride.audioMuted` | `StreamBroker.setAudioMuted` drops the audio plane at the source: no `type:'audio'` packet leaves `fanOutEncoded`, no RTP reaches the restreamer, and the restreamer serves the video-only SDP |
|
|
618
|
+
*
|
|
619
|
+
* ## `device-audio` and `broker-audio` are two functions, not two knobs
|
|
620
|
+
*
|
|
621
|
+
* They look adjacent and they are not the same control ([D83](../../../../docs/decisions/adr-0083.md)):
|
|
622
|
+
* `device-audio` writes the CAMERA, so it is hardware privacy — the microphone
|
|
623
|
+
* genuinely stops, it survives CamStack entirely, and it costs a multi-second
|
|
624
|
+
* encoder restart on every flip. `broker-audio` writes THIS server, so it is
|
|
625
|
+
* instant, vendor-independent and reversible without touching the camera, and
|
|
626
|
+
* a camera that ignores or lacks the ISAPI/Reolink control is still silenced.
|
|
627
|
+
* D62 forbids a second switch that *disagrees* with the first; these two
|
|
628
|
+
* cannot disagree, because neither reads the other's store — the camera holds
|
|
629
|
+
* one, the broker holds the other, and each reports its own fact.
|
|
994
630
|
*
|
|
995
631
|
* ## The two switches whose authority is not on this server
|
|
996
632
|
*
|
|
@@ -1052,6 +688,7 @@ var CameraSwitchIdSchema = zod.z.enum([
|
|
|
1052
688
|
"object-detection",
|
|
1053
689
|
"privacy-mask",
|
|
1054
690
|
"device-audio",
|
|
691
|
+
"broker-audio",
|
|
1055
692
|
"audio-analysis",
|
|
1056
693
|
"recording",
|
|
1057
694
|
"notifications"
|
|
@@ -1067,12 +704,17 @@ var CameraSwitchIdSchema = zod.z.enum([
|
|
|
1067
704
|
* `object-detection` despite feeding it: a mask blanks REGIONS, so its blast
|
|
1068
705
|
* radius is partial, and the "broadest first" rule does not rank a partial
|
|
1069
706
|
* control above a whole-function one.
|
|
707
|
+
*
|
|
708
|
+
* `broker-audio` sits directly BELOW `device-audio` by the same source-first
|
|
709
|
+
* rule: the camera's microphone feeds the broker, so silencing the camera
|
|
710
|
+
* leaves the broker's mute with nothing to suppress; the reverse is not true.
|
|
1070
711
|
*/
|
|
1071
712
|
var CAMERA_SWITCH_ORDER = [
|
|
1072
713
|
"stream-broker",
|
|
1073
714
|
"object-detection",
|
|
1074
715
|
"privacy-mask",
|
|
1075
716
|
"device-audio",
|
|
717
|
+
"broker-audio",
|
|
1076
718
|
"audio-analysis",
|
|
1077
719
|
"recording",
|
|
1078
720
|
"notifications"
|
|
@@ -1098,7 +740,8 @@ var CameraSwitchAuthoritySchema = zod.z.discriminatedUnion("kind", [
|
|
|
1098
740
|
zod.z.object({
|
|
1099
741
|
kind: zod.z.literal("camera-mask"),
|
|
1100
742
|
capName: zod.z.string()
|
|
1101
|
-
})
|
|
743
|
+
}),
|
|
744
|
+
zod.z.object({ kind: zod.z.literal("broker-audio-mute") })
|
|
1102
745
|
]);
|
|
1103
746
|
/**
|
|
1104
747
|
* Why a switch is not offered for this camera. Rendered instead of the
|
|
@@ -1197,6 +840,13 @@ var CAMERA_SWITCH_CATALOG = {
|
|
|
1197
840
|
},
|
|
1198
841
|
countsAsSwitchedOff: true
|
|
1199
842
|
},
|
|
843
|
+
"broker-audio": {
|
|
844
|
+
id: "broker-audio",
|
|
845
|
+
label: "Audio distribution",
|
|
846
|
+
costWhenOff: "Off: this server distributes no sound for this camera — live view is silent and everything recorded while it is off is silent FOREVER, even after you turn it back on. Audio detection and classification also have nothing to analyse. The camera keeps capturing sound, so nothing about the camera changes and turning this back on is instant, with no interruption to the picture.",
|
|
847
|
+
authority: { kind: "broker-audio-mute" },
|
|
848
|
+
countsAsSwitchedOff: true
|
|
849
|
+
},
|
|
1200
850
|
"audio-analysis": {
|
|
1201
851
|
id: "audio-analysis",
|
|
1202
852
|
label: "Audio detection & classification",
|
|
@@ -1315,6 +965,18 @@ function resolveState(descriptor, input) {
|
|
|
1315
965
|
enabled: mask.enabled
|
|
1316
966
|
};
|
|
1317
967
|
}
|
|
968
|
+
case "broker-audio-mute": {
|
|
969
|
+
const broker = input.brokerAudio;
|
|
970
|
+
if (broker === null) return {
|
|
971
|
+
available: false,
|
|
972
|
+
enabled: true,
|
|
973
|
+
unavailableReason: "source-unreachable"
|
|
974
|
+
};
|
|
975
|
+
return {
|
|
976
|
+
available: true,
|
|
977
|
+
enabled: !broker.muted
|
|
978
|
+
};
|
|
979
|
+
}
|
|
1318
980
|
}
|
|
1319
981
|
}
|
|
1320
982
|
/**
|
|
@@ -4756,7 +4418,44 @@ var streamBrokerCapability = {
|
|
|
4756
4418
|
kind: "mutation",
|
|
4757
4419
|
auth: "admin"
|
|
4758
4420
|
}),
|
|
4759
|
-
isRtspEnabled: require_sleep.method(zod.z.object({ brokerId: zod.z.string() }), zod.z.boolean())
|
|
4421
|
+
isRtspEnabled: require_sleep.method(zod.z.object({ brokerId: zod.z.string() }), zod.z.boolean()),
|
|
4422
|
+
/**
|
|
4423
|
+
* ── Per-device audio-plane policy (D83) ───────────────────────────
|
|
4424
|
+
*
|
|
4425
|
+
* The BROKER-side mute: while `muted`, this node distributes none of
|
|
4426
|
+
* the device's audio, on any plane it serves — live (WebRTC / encoded
|
|
4427
|
+
* subscribers) AND recording (the RTSP restreamer the recorder pulls,
|
|
4428
|
+
* which additionally serves the video-only SDP so the recorder's ffmpeg
|
|
4429
|
+
* never declares an audio stream it will not receive).
|
|
4430
|
+
*
|
|
4431
|
+
* Keyed by `deviceId`, not `brokerId`: every one of a camera's streams
|
|
4432
|
+
* carries the same microphone, and a per-stream answer would let main
|
|
4433
|
+
* and sub disagree about whether the camera is silent.
|
|
4434
|
+
*
|
|
4435
|
+
* The state lives in the broker's existing `DeviceOverride` blob — no
|
|
4436
|
+
* new store — and the mute is applied to a fresh broker at creation, so
|
|
4437
|
+
* a restart, a re-dial or a catalog republish never un-mutes a camera.
|
|
4438
|
+
*/
|
|
4439
|
+
getDeviceAudioMute: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int() }), zod.z.object({
|
|
4440
|
+
muted: zod.z.boolean(),
|
|
4441
|
+
/**
|
|
4442
|
+
* How many live non-derived brokers currently hold the mute. Purely
|
|
4443
|
+
* diagnostic: `muted` is the policy and is authoritative on its own
|
|
4444
|
+
* (it applies to brokers that do not exist yet), while this says
|
|
4445
|
+
* whether anything is presently being silenced.
|
|
4446
|
+
*/
|
|
4447
|
+
appliedBrokers: zod.z.number().int().nonnegative()
|
|
4448
|
+
})),
|
|
4449
|
+
setDeviceAudioMute: require_sleep.method(zod.z.object({
|
|
4450
|
+
deviceId: zod.z.number().int(),
|
|
4451
|
+
muted: zod.z.boolean()
|
|
4452
|
+
}), zod.z.object({
|
|
4453
|
+
muted: zod.z.boolean(),
|
|
4454
|
+
appliedBrokers: zod.z.number().int().nonnegative()
|
|
4455
|
+
}), {
|
|
4456
|
+
kind: "mutation",
|
|
4457
|
+
auth: "admin"
|
|
4458
|
+
})
|
|
4760
4459
|
},
|
|
4761
4460
|
events: {
|
|
4762
4461
|
onCamStreamDemand: require_sleep.event(zod.z.object({
|
|
@@ -12603,6 +12302,30 @@ var TrackSourceSchema = zod.z.enum([
|
|
|
12603
12302
|
"audio"
|
|
12604
12303
|
]);
|
|
12605
12304
|
/**
|
|
12305
|
+
* Where a track sits in the RETRAIN lifecycle (D81).
|
|
12306
|
+
*
|
|
12307
|
+
* - `none` — never marked, or un-marked. Evictable.
|
|
12308
|
+
* - `staging` — the operator wants this track as training material and has not
|
|
12309
|
+
* finished with it. **This is the only state retention holds**: the track and
|
|
12310
|
+
* everything it owns (object events, crops, keyframes, CLIP vector) survive
|
|
12311
|
+
* the device's age window.
|
|
12312
|
+
* - `trained` — the retrain page has taken what it needed. The frames it chose
|
|
12313
|
+
* were COPIED into the retrain dataset at selection time, so the dataset no
|
|
12314
|
+
* longer depends on the track's media and the track becomes EVICTABLE again.
|
|
12315
|
+
* Terminal for the plain `markForTrain` toggle: returning it to `staging` is
|
|
12316
|
+
* a deliberate action of the retrain page, not a side effect of a checkbox.
|
|
12317
|
+
*
|
|
12318
|
+
* There is no `null`. The state is stored `TEXT NOT NULL DEFAULT 'none'` because
|
|
12319
|
+
* the store's filter language has only positive equality and `whereIn` — no
|
|
12320
|
+
* negation, no IS NULL — so a NULL would be unselectable by ANY predicate and
|
|
12321
|
+
* would make the entire pre-column history immortal in one deploy.
|
|
12322
|
+
*/
|
|
12323
|
+
var RetrainStatusSchema = zod.z.enum([
|
|
12324
|
+
"none",
|
|
12325
|
+
"staging",
|
|
12326
|
+
"trained"
|
|
12327
|
+
]);
|
|
12328
|
+
/**
|
|
12606
12329
|
* Per-track OPERATOR flags — set by hand from the admin UI or the viewer, never
|
|
12607
12330
|
* by the pipeline. Spread into `TrackSchema` and `KeyEventSchema` from one place
|
|
12608
12331
|
* so the two surfaces cannot drift.
|
|
@@ -12612,18 +12335,31 @@ var TrackSourceSchema = zod.z.enum([
|
|
|
12612
12335
|
* columns existed read as absent, and a consumer that needs a boolean should say
|
|
12613
12336
|
* `flag === true`, not `flag !== false`.
|
|
12614
12337
|
*
|
|
12615
|
-
*
|
|
12616
|
-
*
|
|
12617
|
-
*
|
|
12618
|
-
* `
|
|
12338
|
+
* `markForTrain` is the WIRE FACE of {@link RetrainStatusSchema}, not a column:
|
|
12339
|
+
* it is exactly `retrainStatus === 'staging'`, in both directions. Writing
|
|
12340
|
+
* `true` moves `none → staging`, writing `false` moves `staging → none`, and a
|
|
12341
|
+
* `trained` track reports `false` while refusing both writes. The boolean is
|
|
12342
|
+
* kept because three surfaces drive a toggle off it; anything that needs to tell
|
|
12343
|
+
* "never marked" from "already trained" must read `retrainStatus`.
|
|
12344
|
+
*
|
|
12345
|
+
* `debug` does NOT pin; it is attention, not durability.
|
|
12619
12346
|
*/
|
|
12620
12347
|
var TrackFlagFields = {
|
|
12621
|
-
/** Operator marked this track as training material.
|
|
12348
|
+
/** Operator marked this track as training material — i.e. `retrainStatus` is
|
|
12349
|
+
* `'staging'`. */
|
|
12622
12350
|
markForTrain: zod.z.boolean().optional(),
|
|
12623
12351
|
/** Operator marked this track for diagnostic attention. */
|
|
12624
12352
|
debug: zod.z.boolean().optional()
|
|
12625
12353
|
};
|
|
12626
12354
|
/**
|
|
12355
|
+
* The lifecycle field itself, on the READ surfaces only (`Track`, `KeyEvent`).
|
|
12356
|
+
* Deliberately NOT part of {@link TrackFlagFields}: that group also builds the
|
|
12357
|
+
* write patch, and the status is not something the toggle sets — it is what the
|
|
12358
|
+
* toggle's boolean is derived from. Absent on an in-RAM track never touched;
|
|
12359
|
+
* always present on a persisted row (the column default materialises `'none'`).
|
|
12360
|
+
*/
|
|
12361
|
+
var TrackRetrainFields = { retrainStatus: RetrainStatusSchema.optional() };
|
|
12362
|
+
/**
|
|
12627
12363
|
* The write half: a PARTIAL patch. An omitted key is left untouched, so setting
|
|
12628
12364
|
* one flag can never clear the other — the toggles are independent and are
|
|
12629
12365
|
* driven from three surfaces that do not know about each other.
|
|
@@ -12637,13 +12373,108 @@ var TrackFlagsPatchSchema = zod.z.object(TrackFlagFields);
|
|
|
12637
12373
|
var TrackFlagsSchema = zod.z.object({
|
|
12638
12374
|
trackId: zod.z.string(),
|
|
12639
12375
|
markForTrain: zod.z.boolean(),
|
|
12640
|
-
debug: zod.z.boolean()
|
|
12376
|
+
debug: zod.z.boolean(),
|
|
12377
|
+
/** The lifecycle state the boolean was derived from. Required here (unlike on
|
|
12378
|
+
* a track row) because this shape is only ever produced by the write body,
|
|
12379
|
+
* which always knows it — and a surface that has just written needs to render
|
|
12380
|
+
* `trained` without a re-fetch. */
|
|
12381
|
+
retrainStatus: RetrainStatusSchema
|
|
12382
|
+
});
|
|
12383
|
+
/**
|
|
12384
|
+
* WHICH tier a label occupies. The slot a label lands in is DECLARED by the
|
|
12385
|
+
* step that produced it (`StepDefinition.labelTier`), never inferred from the
|
|
12386
|
+
* text or the step's name.
|
|
12387
|
+
*
|
|
12388
|
+
* - `1` — a SUB-CLASS: finer than the macro class, still a taxonomy token.
|
|
12389
|
+
* `animal-type` (`dog`, `bird`), `vehicle-type` (`van`), and the root
|
|
12390
|
+
* detector's own raw class when it is finer than the macro it maps to.
|
|
12391
|
+
* - `2` — an INSTANCE: the finest thing said about this subject.
|
|
12392
|
+
* `species` (`Turdus migratorius`), `identity` (`Alice`), `plate-text`.
|
|
12393
|
+
*
|
|
12394
|
+
* The macro class itself (`person`, `vehicle`, `animal`, `package`, `face`,
|
|
12395
|
+
* `plate`, `audio`) is NOT a tier — it is `className`, and a macro token
|
|
12396
|
+
* offered for either label slot is refused (2026-08-07 rule; the refusal is
|
|
12397
|
+
* logged as `label tier collapse refused`).
|
|
12398
|
+
*/
|
|
12399
|
+
var LabelTierSchema = zod.z.union([zod.z.literal(1), zod.z.literal(2)]);
|
|
12400
|
+
/**
|
|
12401
|
+
* WHO decided a label, and when. Carried per tier so a value can be traced to
|
|
12402
|
+
* the step and model that produced it — which is what makes the write rule
|
|
12403
|
+
* arguable after the fact ("why is 592's label `dog` and not `Canis lupus`?")
|
|
12404
|
+
* and what lets a migrated, UNATTRIBUTED value be told apart from a real one.
|
|
12405
|
+
*
|
|
12406
|
+
* `stepId` is the pipeline step id (`animal-classifier`, `bird-classifier`,
|
|
12407
|
+
* `plate-ocr`, `face-embedding`, `object-detection`), or the sentinel
|
|
12408
|
+
* `migration:4g` for a value the 4g migration moved from the single-slot era —
|
|
12409
|
+
* that value has no provenance, and the write rule lets ANY properly-attributed
|
|
12410
|
+
* write of the same tier replace it regardless of score.
|
|
12411
|
+
*/
|
|
12412
|
+
var LabelAttributionSchema = zod.z.object({
|
|
12413
|
+
stepId: zod.z.string(),
|
|
12414
|
+
modelId: zod.z.string().optional(),
|
|
12415
|
+
decidedAt: zod.z.number()
|
|
12416
|
+
});
|
|
12417
|
+
/**
|
|
12418
|
+
* The TIERED label model (roadmap 4g), spread into `TrackSchema` and
|
|
12419
|
+
* `ObjectEventSchema` from ONE place so the two surfaces cannot drift — a
|
|
12420
|
+
* track and its events always answer the same question the same way.
|
|
12421
|
+
*
|
|
12422
|
+
* Two scalar columns, not an array: every consumer wants "the coarse one" or
|
|
12423
|
+
* "the fine one", and an array made both a scan. `label` is tier 1, `subLabel`
|
|
12424
|
+
* is tier 2, and each carries its own score + attribution.
|
|
12425
|
+
*
|
|
12426
|
+
* **Reading it.** What a human should be shown is `subLabel ?? label` — the
|
|
12427
|
+
* finest thing known. Before 4g the single `label` column held the finest
|
|
12428
|
+
* value, so a consumer that has not been updated reads the tier-1 slot and
|
|
12429
|
+
* shows nothing on a species-only row; that is why the migration puts every
|
|
12430
|
+
* pre-4g value in tier 2 (it cannot regress a display that reads the fallback)
|
|
12431
|
+
* and why the read surfaces were changed in the same train.
|
|
12432
|
+
*
|
|
12433
|
+
* **Writing it.** The slots are independent, which is the whole point: a
|
|
12434
|
+
* tier-1 write (`bird`) can never overwrite a tier-2 value (`Turdus
|
|
12435
|
+
* migratorius`), so fineness cannot regress by construction. Within a tier the
|
|
12436
|
+
* higher score wins. One rule, one implementation — see
|
|
12437
|
+
* `pipeline/label-tier.ts` in addon-post-analysis.
|
|
12438
|
+
*/
|
|
12439
|
+
var TieredLabelFields = {
|
|
12440
|
+
/** Tier 1 — the sub-class. See {@link LabelTierSchema}. */
|
|
12441
|
+
label: zod.z.string().optional(),
|
|
12442
|
+
/** Confidence of the tier-1 value, as reported by the deciding step. */
|
|
12443
|
+
labelScore: zod.z.number().optional(),
|
|
12444
|
+
/** Provenance of the tier-1 value. See {@link LabelAttributionSchema}. */
|
|
12445
|
+
labelMeta: LabelAttributionSchema.optional(),
|
|
12446
|
+
/** Tier 2 — the instance. See {@link LabelTierSchema}. */
|
|
12447
|
+
subLabel: zod.z.string().optional(),
|
|
12448
|
+
/** Confidence of the tier-2 value, as reported by the deciding step. */
|
|
12449
|
+
subLabelScore: zod.z.number().optional(),
|
|
12450
|
+
/** Provenance of the tier-2 value. See {@link LabelAttributionSchema}. */
|
|
12451
|
+
subLabelMeta: LabelAttributionSchema.optional()
|
|
12452
|
+
};
|
|
12453
|
+
/** Per-camera slice of a training-export estimate. */
|
|
12454
|
+
var TrainingExportDeviceTotalsSchema = zod.z.object({
|
|
12455
|
+
deviceId: zod.z.number(),
|
|
12456
|
+
tracks: zod.z.number().int(),
|
|
12457
|
+
files: zod.z.number().int(),
|
|
12458
|
+
bytes: zod.z.number().int()
|
|
12459
|
+
});
|
|
12460
|
+
/**
|
|
12461
|
+
* What a training export WOULD contain. Computed from media index rows only —
|
|
12462
|
+
* no blob is read to produce this.
|
|
12463
|
+
*/
|
|
12464
|
+
var TrainingExportSummarySchema = zod.z.object({
|
|
12465
|
+
generatedAt: zod.z.number(),
|
|
12466
|
+
trackCount: zod.z.number().int(),
|
|
12467
|
+
fileCount: zod.z.number().int(),
|
|
12468
|
+
byteCount: zod.z.number().int(),
|
|
12469
|
+
/** More marked tracks exist than a single pass carries. */
|
|
12470
|
+
truncated: zod.z.boolean(),
|
|
12471
|
+
devices: zod.z.array(TrainingExportDeviceTotalsSchema).readonly()
|
|
12641
12472
|
});
|
|
12642
12473
|
var TrackSchema = zod.z.object({
|
|
12643
12474
|
trackId: zod.z.string(),
|
|
12644
12475
|
deviceId: zod.z.number(),
|
|
12645
12476
|
className: zod.z.string(),
|
|
12646
|
-
|
|
12477
|
+
...TieredLabelFields,
|
|
12647
12478
|
producingDeviceName: zod.z.string().optional(),
|
|
12648
12479
|
/** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
|
|
12649
12480
|
source: TrackSourceSchema.optional(),
|
|
@@ -12682,7 +12513,8 @@ var TrackSchema = zod.z.object({
|
|
|
12682
12513
|
* Populated from the persisted envelope columns on historical reads;
|
|
12683
12514
|
* absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
|
|
12684
12515
|
envelope: TrackEnvelopeSchema.optional(),
|
|
12685
|
-
...TrackFlagFields
|
|
12516
|
+
...TrackFlagFields,
|
|
12517
|
+
...TrackRetrainFields
|
|
12686
12518
|
});
|
|
12687
12519
|
var BaseEventFields = {
|
|
12688
12520
|
id: zod.z.string(),
|
|
@@ -12763,7 +12595,7 @@ var ObjectEventSchema = zod.z.object({
|
|
|
12763
12595
|
/** Omitted in slim projection. */
|
|
12764
12596
|
trackId: zod.z.string().optional(),
|
|
12765
12597
|
className: zod.z.string(),
|
|
12766
|
-
|
|
12598
|
+
...TieredLabelFields,
|
|
12767
12599
|
/** Omitted in slim projection. */
|
|
12768
12600
|
confidence: zod.z.number().optional(),
|
|
12769
12601
|
/** Heavy JSON — omitted in slim projection. */
|
|
@@ -12844,6 +12676,173 @@ var MediaFileSchema = zod.z.object({
|
|
|
12844
12676
|
* stored blob and a `?variant=thumb` rendering without fetching either.
|
|
12845
12677
|
*/
|
|
12846
12678
|
var MediaFileInfoSchema = MediaFileSchema.omit({ base64: true });
|
|
12679
|
+
/**
|
|
12680
|
+
* The MACRO tier of an annotation — a CLOSED set.
|
|
12681
|
+
*
|
|
12682
|
+
* This is what the exported detector predicts, so a typo here is a new class
|
|
12683
|
+
* with one example in it. `label` and `subLabel` are open strings by contrast:
|
|
12684
|
+
* the whole point of the page is teaching the model things it does not know
|
|
12685
|
+
* yet, and constraining that vocabulary would make it useless.
|
|
12686
|
+
*
|
|
12687
|
+
* A macro class is NEVER a label. The provider refuses a write whose `label` or
|
|
12688
|
+
* `subLabel` is one of these values, in any casing, because once `person`
|
|
12689
|
+
* exists in both tiers "every person box" stops being answerable without
|
|
12690
|
+
* knowing every string anyone ever typed — and the damage is retroactive.
|
|
12691
|
+
*/
|
|
12692
|
+
var RetrainMacroClassSchema = zod.z.enum([
|
|
12693
|
+
"person",
|
|
12694
|
+
"vehicle",
|
|
12695
|
+
"animal",
|
|
12696
|
+
"package",
|
|
12697
|
+
"face",
|
|
12698
|
+
"plate"
|
|
12699
|
+
]);
|
|
12700
|
+
/** A subject to learn, or a phantom to unlearn (taught by OMISSION). */
|
|
12701
|
+
var RetrainAnnotationKindSchema = zod.z.enum(["subject", "model_error"]);
|
|
12702
|
+
/** Did a human draw this box, or did the assist propose it? */
|
|
12703
|
+
var RetrainAnnotationSourceSchema = zod.z.enum(["operator", "assist"]);
|
|
12704
|
+
/** Normalised `[0,1]` rectangle against the FULL frame — the canonical form. */
|
|
12705
|
+
var RetrainBboxSchema = zod.z.object({
|
|
12706
|
+
x: zod.z.number(),
|
|
12707
|
+
y: zod.z.number(),
|
|
12708
|
+
w: zod.z.number(),
|
|
12709
|
+
h: zod.z.number()
|
|
12710
|
+
});
|
|
12711
|
+
/**
|
|
12712
|
+
* One annotated subject.
|
|
12713
|
+
*
|
|
12714
|
+
* `bbox` is normalised against the full frame, ALWAYS. The per-model shapes
|
|
12715
|
+
* (letterboxed root / zone-cropped package / subject-cropped classifier) are
|
|
12716
|
+
* derived from it at export and never stored — storing them is how one feature
|
|
12717
|
+
* space ends up holding two crops of the same subject (D52).
|
|
12718
|
+
*/
|
|
12719
|
+
var RetrainAnnotationSchema = zod.z.object({
|
|
12720
|
+
id: zod.z.string(),
|
|
12721
|
+
trackId: zod.z.string(),
|
|
12722
|
+
deviceId: zod.z.number(),
|
|
12723
|
+
/** The COPY in retrain storage — never the source track's media key. */
|
|
12724
|
+
mediaKey: zod.z.string(),
|
|
12725
|
+
bbox: RetrainBboxSchema,
|
|
12726
|
+
macroClass: RetrainMacroClassSchema,
|
|
12727
|
+
label: zod.z.string().optional(),
|
|
12728
|
+
subLabel: zod.z.string().optional(),
|
|
12729
|
+
kind: RetrainAnnotationKindSchema,
|
|
12730
|
+
source: RetrainAnnotationSourceSchema,
|
|
12731
|
+
/** Which model proposed this box — or, on a `model_error`, drew the phantom. */
|
|
12732
|
+
assistModelId: zod.z.string().optional(),
|
|
12733
|
+
assistScore: zod.z.number().optional(),
|
|
12734
|
+
exportedInBatch: zod.z.string().optional(),
|
|
12735
|
+
createdAt: zod.z.number()
|
|
12736
|
+
});
|
|
12737
|
+
/** The write form — the server owns `id`, `createdAt` and the frame binding. */
|
|
12738
|
+
var RetrainAnnotationDraftSchema = RetrainAnnotationSchema.omit({
|
|
12739
|
+
id: true,
|
|
12740
|
+
trackId: true,
|
|
12741
|
+
deviceId: true,
|
|
12742
|
+
mediaKey: true,
|
|
12743
|
+
createdAt: true,
|
|
12744
|
+
exportedInBatch: true
|
|
12745
|
+
});
|
|
12746
|
+
/** A track sitting in `staging`, with everything the worklist needs to rank it. */
|
|
12747
|
+
var RetrainTrackSchema = zod.z.object({
|
|
12748
|
+
trackId: zod.z.string(),
|
|
12749
|
+
deviceId: zod.z.number(),
|
|
12750
|
+
className: zod.z.string(),
|
|
12751
|
+
label: zod.z.string().optional(),
|
|
12752
|
+
firstSeen: zod.z.number(),
|
|
12753
|
+
lastSeen: zod.z.number(),
|
|
12754
|
+
/** How many frames the dataset already holds from this track. */
|
|
12755
|
+
frameCount: zod.z.number().int(),
|
|
12756
|
+
/** How many subjects have been annotated on those frames. `0` with
|
|
12757
|
+
* `frameCount: 0` is exactly "staging, still to work". */
|
|
12758
|
+
annotationCount: zod.z.number().int()
|
|
12759
|
+
});
|
|
12760
|
+
/** A frame the picker may offer — an index row, no blob was read to produce it. */
|
|
12761
|
+
var RetrainFrameCandidateSchema = zod.z.object({
|
|
12762
|
+
mediaKey: zod.z.string(),
|
|
12763
|
+
kind: MediaFileKindEnum,
|
|
12764
|
+
timestamp: zod.z.number(),
|
|
12765
|
+
sizeBytes: zod.z.number().int(),
|
|
12766
|
+
/** A copy of this original already exists — selecting it is free and cannot
|
|
12767
|
+
* fail, whatever became of the original. */
|
|
12768
|
+
copied: zod.z.boolean()
|
|
12769
|
+
});
|
|
12770
|
+
/** A frame the dataset OWNS: bytes copied at selection time. */
|
|
12771
|
+
var RetrainFrameSchema = zod.z.object({
|
|
12772
|
+
frameId: zod.z.string(),
|
|
12773
|
+
deviceId: zod.z.number(),
|
|
12774
|
+
trackId: zod.z.string(),
|
|
12775
|
+
/** Provenance only. It may already point at nothing — that is expected. */
|
|
12776
|
+
sourceMediaKey: zod.z.string(),
|
|
12777
|
+
sourceKind: MediaFileKindEnum,
|
|
12778
|
+
sizeBytes: zod.z.number().int(),
|
|
12779
|
+
width: zod.z.number().int(),
|
|
12780
|
+
height: zod.z.number().int(),
|
|
12781
|
+
copiedAt: zod.z.number()
|
|
12782
|
+
});
|
|
12783
|
+
/** Why a copy-on-select could not be honoured — named, never a silent skip. */
|
|
12784
|
+
var RetrainCopyRefusalSchema = zod.z.enum([
|
|
12785
|
+
"source-missing",
|
|
12786
|
+
"unreadable-image",
|
|
12787
|
+
"write-failed"
|
|
12788
|
+
]);
|
|
12789
|
+
var RetrainFrameSelectionSchema = zod.z.object({
|
|
12790
|
+
copied: zod.z.array(RetrainFrameSchema).readonly(),
|
|
12791
|
+
refused: zod.z.array(zod.z.object({
|
|
12792
|
+
sourceMediaKey: zod.z.string(),
|
|
12793
|
+
reason: RetrainCopyRefusalSchema
|
|
12794
|
+
})).readonly()
|
|
12795
|
+
});
|
|
12796
|
+
var RetrainFrameListSchema = zod.z.object({
|
|
12797
|
+
candidates: zod.z.array(RetrainFrameCandidateSchema).readonly(),
|
|
12798
|
+
copies: zod.z.array(RetrainFrameSchema).readonly(),
|
|
12799
|
+
/** What the page pre-selects — the native key frame when one survives. */
|
|
12800
|
+
autoPickMediaKey: zod.z.string().optional()
|
|
12801
|
+
});
|
|
12802
|
+
/** What the operator asked the assist to look for. */
|
|
12803
|
+
var RetrainAssistSubjectSchema = zod.z.discriminatedUnion("kind", [zod.z.object({
|
|
12804
|
+
kind: zod.z.literal("package"),
|
|
12805
|
+
zone: RetrainBboxSchema.optional()
|
|
12806
|
+
}), zod.z.object({
|
|
12807
|
+
kind: zod.z.literal("objects"),
|
|
12808
|
+
modelId: zod.z.string(),
|
|
12809
|
+
minScore: zod.z.number().optional()
|
|
12810
|
+
})]);
|
|
12811
|
+
/**
|
|
12812
|
+
* The assist's answer — a discriminated union, because "the model saw nothing"
|
|
12813
|
+
* and "this node cannot run that model" lead to different next moves and a
|
|
12814
|
+
* nullable result cannot tell them apart.
|
|
12815
|
+
*/
|
|
12816
|
+
var RetrainAssistResultSchema = zod.z.discriminatedUnion("kind", [zod.z.object({
|
|
12817
|
+
kind: zod.z.literal("proposed"),
|
|
12818
|
+
modelId: zod.z.string(),
|
|
12819
|
+
stepId: zod.z.string(),
|
|
12820
|
+
minScore: zod.z.number(),
|
|
12821
|
+
/** Drafts, ready to edit. `source: 'assist'` until the operator touches one. */
|
|
12822
|
+
proposals: zod.z.array(RetrainAnnotationDraftSchema).readonly(),
|
|
12823
|
+
/** Returned by the runner but removed by the threshold. */
|
|
12824
|
+
belowThreshold: zod.z.number().int()
|
|
12825
|
+
}), zod.z.object({
|
|
12826
|
+
kind: zod.z.literal("refused"),
|
|
12827
|
+
/** `no-zone` is ours; the rest are the runner's own refusal vocabulary. */
|
|
12828
|
+
reason: zod.z.string(),
|
|
12829
|
+
detail: zod.z.string().optional()
|
|
12830
|
+
})]);
|
|
12831
|
+
/** The outcome of a lifecycle move owned by the retrain page. */
|
|
12832
|
+
var RetrainTransitionResultSchema = zod.z.object({
|
|
12833
|
+
trackId: zod.z.string(),
|
|
12834
|
+
/** Where the track ended up, whatever happened. */
|
|
12835
|
+
retrainStatus: RetrainStatusSchema,
|
|
12836
|
+
/** `false` ⇒ the move was refused or was a no-op; `reason` says which. */
|
|
12837
|
+
changed: zod.z.boolean(),
|
|
12838
|
+
reason: zod.z.enum([
|
|
12839
|
+
"unknown-track",
|
|
12840
|
+
"no-frames-copied",
|
|
12841
|
+
"not-staging",
|
|
12842
|
+
"not-trained",
|
|
12843
|
+
"unchanged"
|
|
12844
|
+
]).optional()
|
|
12845
|
+
});
|
|
12847
12846
|
var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
|
|
12848
12847
|
var MAX_EVENT_QUERY_LIMIT = 5e3;
|
|
12849
12848
|
var DeviceEventQueryInput = zod.z.object({
|
|
@@ -12898,13 +12897,14 @@ var KeyEventSchema = zod.z.object({
|
|
|
12898
12897
|
/** Track start time (firstSeen). */
|
|
12899
12898
|
timestamp: zod.z.number(),
|
|
12900
12899
|
className: zod.z.string(),
|
|
12901
|
-
|
|
12900
|
+
...TieredLabelFields,
|
|
12902
12901
|
importance: zod.z.number(),
|
|
12903
12902
|
/** Highest-confidence ObjectEvent id for the track (empty when none). */
|
|
12904
12903
|
bestEventId: zod.z.string(),
|
|
12905
12904
|
/** Track lifetime in ms (lastSeen - firstSeen). */
|
|
12906
12905
|
windowMs: zod.z.number().optional(),
|
|
12907
|
-
...TrackFlagFields
|
|
12906
|
+
...TrackFlagFields,
|
|
12907
|
+
...TrackRetrainFields
|
|
12908
12908
|
});
|
|
12909
12909
|
var TrackedDetectionSchema = zod.z.object({
|
|
12910
12910
|
trackId: zod.z.string(),
|
|
@@ -13266,11 +13266,29 @@ var pipelineAnalyticsCapability = {
|
|
|
13266
13266
|
*
|
|
13267
13267
|
* `auth: 'protected'` (the default), NOT `admin`: the viewer is an
|
|
13268
13268
|
* authenticated non-admin surface and two of the three call sites are
|
|
13269
|
-
* there.
|
|
13270
|
-
*
|
|
13269
|
+
* there. The note that used to sit here said to revisit this the day a flag
|
|
13270
|
+
* gained an effect that costs storage, and D81 is that day — `markForTrain`
|
|
13271
|
+
* now pins. It STAYS protected, and the reason is that the alternative
|
|
13272
|
+
* makes the feature pointless: marking a track is something you do while
|
|
13273
|
+
* looking at it, on the surface you were already looking at it on, and that
|
|
13274
|
+
* surface is the viewer. What the storage cost gets instead is a BOUND — a
|
|
13275
|
+
* per-device pin budget enforced in the body, refusing a new pin past the
|
|
13276
|
+
* limit while always allowing un-marking. `deleteTracks` next door is still
|
|
13277
|
+
* admin, because destroying evidence and preserving it are not symmetric.
|
|
13271
13278
|
*
|
|
13272
|
-
*
|
|
13273
|
-
*
|
|
13279
|
+
* `markForTrain` writes the retrain LIFECYCLE, not a boolean column: `true`
|
|
13280
|
+
* is `none → staging`, `false` is `staging → none`. A track already
|
|
13281
|
+
* `trained` refuses BOTH — its frames are copies inside the retrain dataset
|
|
13282
|
+
* and re-staging it from a generic toggle is how the same material gets
|
|
13283
|
+
* annotated twice under two ground truths. Returning a trained track to
|
|
13284
|
+
* staging is a deliberate action of the retrain page, which is also the only
|
|
13285
|
+
* thing that produces `trained` in the first place.
|
|
13286
|
+
*
|
|
13287
|
+
* Returns the RESOLVED state of both flags (absent → `false`) plus the
|
|
13288
|
+
* `retrainStatus` they were derived from, so a caller can drive its toggle
|
|
13289
|
+
* — and render a `trained` badge — without a re-fetch. Rejects an unknown
|
|
13290
|
+
* track, a new staging mark on a device already holding its full budget, and
|
|
13291
|
+
* any `markForTrain` write against a trained track.
|
|
13274
13292
|
*/
|
|
13275
13293
|
setTrackFlags: require_sleep.method(zod.z.object({
|
|
13276
13294
|
/** Log/audit scope only — the trackId is globally unique on its own. */
|
|
@@ -13334,6 +13352,237 @@ var pipelineAnalyticsCapability = {
|
|
|
13334
13352
|
kind: "query",
|
|
13335
13353
|
auth: "admin"
|
|
13336
13354
|
}),
|
|
13355
|
+
/**
|
|
13356
|
+
* The CHEAP QUESTION, asked before any media moves: how big is the dataset
|
|
13357
|
+
* the marked (`markForTrain`) tracks would produce?
|
|
13358
|
+
*
|
|
13359
|
+
* Answered from media INDEX rows only — key, kind, size, timestamp — so it
|
|
13360
|
+
* costs ~2 KB of reads per track and no blob reads at all. The measured harm
|
|
13361
|
+
* behind D56 was a bulk pass that read and base64'd every blob a track owned
|
|
13362
|
+
* before deciding anything, taking hub-main to 82 s busy out of 120; an
|
|
13363
|
+
* export is that same I/O shape, so it inherits the same discipline: know
|
|
13364
|
+
* the size, then decide.
|
|
13365
|
+
*
|
|
13366
|
+
* `truncated` reports that more marked tracks exist than one pass carries.
|
|
13367
|
+
* Empty `deviceIds` ⇒ every device that has marked tracks.
|
|
13368
|
+
*/
|
|
13369
|
+
getTrainingExportSummary: require_sleep.method(zod.z.object({ deviceIds: zod.z.array(zod.z.number()).optional() }), TrainingExportSummarySchema, {
|
|
13370
|
+
kind: "query",
|
|
13371
|
+
auth: "admin"
|
|
13372
|
+
}),
|
|
13373
|
+
/**
|
|
13374
|
+
* Where to download the dataset archive.
|
|
13375
|
+
*
|
|
13376
|
+
* The BYTES do not come back through this cap — they come from the returned
|
|
13377
|
+
* data-plane URL, which streams a tar built entry by entry. A multi-gigabyte
|
|
13378
|
+
* archive base64'd through a unary RPC envelope would be held whole in
|
|
13379
|
+
* memory twice on a hub this repo has already OOM'd once (D9/D18 are the
|
|
13380
|
+
* same lesson about frames). `getDownloadUrl` on `recordingExport` is the
|
|
13381
|
+
* precedent, and this follows it deliberately.
|
|
13382
|
+
*
|
|
13383
|
+
* The archive contains a `manifest.json` FIRST, then the stored media
|
|
13384
|
+
* VERBATIM under `tracks/<deviceId>/<trackId>/…`. No crop is derived and no
|
|
13385
|
+
* model is run: a training set's pixels must be the pixels the pipeline saw.
|
|
13386
|
+
*/
|
|
13387
|
+
getTrainingExportUrl: require_sleep.method(zod.z.object({ deviceIds: zod.z.array(zod.z.number()).optional() }), zod.z.object({ url: zod.z.string() }), {
|
|
13388
|
+
kind: "query",
|
|
13389
|
+
auth: "admin"
|
|
13390
|
+
}),
|
|
13391
|
+
/**
|
|
13392
|
+
* The staging worklist for one camera, or for every camera that has one.
|
|
13393
|
+
*
|
|
13394
|
+
* Fetched ON DEMAND, over the staging set only — the page never scans
|
|
13395
|
+
* history, because making the working set small is the entire purpose of
|
|
13396
|
+
* the mark. Each row carries how many frames the dataset already holds from
|
|
13397
|
+
* the track and how many subjects were annotated on them, so
|
|
13398
|
+
* `frameCount: 0` reads as "still to work" without a second call per track.
|
|
13399
|
+
*
|
|
13400
|
+
* `auth: 'admin'`, unlike the viewer-level mark itself: marking a track is
|
|
13401
|
+
* curation you do while looking at it, but building the training set the
|
|
13402
|
+
* fleet's models are fine-tuned on is not.
|
|
13403
|
+
*/
|
|
13404
|
+
listRetrainStaging: require_sleep.method(zod.z.object({
|
|
13405
|
+
/** Empty ⇒ every camera that has staging tracks. A LIST, not a single
|
|
13406
|
+
* `deviceId`, deliberately: `deviceId` would make this device-bound and
|
|
13407
|
+
* route it at one camera's owner, and "every camera" would stop being
|
|
13408
|
+
* expressible at all. */
|
|
13409
|
+
deviceIds: zod.z.array(zod.z.number()).optional(),
|
|
13410
|
+
limit: zod.z.number().int().min(1).max(500).optional()
|
|
13411
|
+
}), zod.z.array(RetrainTrackSchema).readonly(), {
|
|
13412
|
+
kind: "query",
|
|
13413
|
+
auth: "admin"
|
|
13414
|
+
}),
|
|
13415
|
+
/**
|
|
13416
|
+
* What a track can contribute, and what it already has.
|
|
13417
|
+
*
|
|
13418
|
+
* `candidates` are the track's whole, unannotated frames — index rows only,
|
|
13419
|
+
* so this is cheap. `copies` are the frames already inside the dataset, and
|
|
13420
|
+
* a candidate whose copy exists is marked `copied: true`: selecting it again
|
|
13421
|
+
* is free and CANNOT fail, whatever became of the original.
|
|
13422
|
+
*
|
|
13423
|
+
* A crop, a thumbnail and `fullFrameBoxed` are never candidates. The last
|
|
13424
|
+
* one matters most: it has the model's own rectangle burned into the pixels,
|
|
13425
|
+
* and a detector trained on it learns to find a green line.
|
|
13426
|
+
*/
|
|
13427
|
+
listRetrainFrames: require_sleep.method(zod.z.object({ trackId: zod.z.string() }), RetrainFrameListSchema, {
|
|
13428
|
+
kind: "query",
|
|
13429
|
+
auth: "admin"
|
|
13430
|
+
}),
|
|
13431
|
+
/**
|
|
13432
|
+
* COPY-ON-SELECT — the write that makes `trained` safe to evict.
|
|
13433
|
+
*
|
|
13434
|
+
* Selecting a frame copies its bytes into retrain storage immediately: not
|
|
13435
|
+
* a reference, not a lease. Once the copy exists the dataset no longer
|
|
13436
|
+
* depends on the track's media, which is exactly what lets D81 hand a
|
|
13437
|
+
* `trained` track back to retention.
|
|
13438
|
+
*
|
|
13439
|
+
* The order inside is load-bearing and is pinned by a test: an EXISTING
|
|
13440
|
+
* copy is returned without touching the source, so an original that
|
|
13441
|
+
* evaporated blocks the selection of THAT ORIGINAL and never the copy
|
|
13442
|
+
* already taken. Every refusal comes back named — a dropped selection is
|
|
13443
|
+
* never silent, on the wire or in the log.
|
|
13444
|
+
*/
|
|
13445
|
+
selectRetrainFrames: require_sleep.method(zod.z.object({
|
|
13446
|
+
deviceId: zod.z.number(),
|
|
13447
|
+
trackId: zod.z.string(),
|
|
13448
|
+
mediaKeys: zod.z.array(zod.z.string()).min(1)
|
|
13449
|
+
}), RetrainFrameSelectionSchema, {
|
|
13450
|
+
kind: "mutation",
|
|
13451
|
+
auth: "admin"
|
|
13452
|
+
}),
|
|
13453
|
+
/** Un-select a frame: its annotations go first, then the copy and its blob.
|
|
13454
|
+
* Deliberately destructive and deliberately explicit — it is the only way
|
|
13455
|
+
* a frame leaves the dataset before export. */
|
|
13456
|
+
deselectRetrainFrame: require_sleep.method(zod.z.object({
|
|
13457
|
+
deviceId: zod.z.number(),
|
|
13458
|
+
trackId: zod.z.string(),
|
|
13459
|
+
frameId: zod.z.string()
|
|
13460
|
+
}), zod.z.object({
|
|
13461
|
+
removed: zod.z.boolean(),
|
|
13462
|
+
removedAnnotations: zod.z.number().int()
|
|
13463
|
+
}), {
|
|
13464
|
+
kind: "mutation",
|
|
13465
|
+
auth: "admin"
|
|
13466
|
+
}),
|
|
13467
|
+
/**
|
|
13468
|
+
* The pixels of ONE copied frame, base64.
|
|
13469
|
+
*
|
|
13470
|
+
* Through the cap rather than a data plane because it is genuinely one
|
|
13471
|
+
* frame at a time, on demand, at human speed — the shape D9/D18 permit
|
|
13472
|
+
* (what they forbid is frames crossing a boundary at frame RATE). The
|
|
13473
|
+
* annotation canvas needs the image and its exact dimensions in the same
|
|
13474
|
+
* answer: a canvas that places a normalised box against a size it guessed
|
|
13475
|
+
* draws every box in the wrong place.
|
|
13476
|
+
*/
|
|
13477
|
+
getRetrainFrameImage: require_sleep.method(zod.z.object({ frameId: zod.z.string() }), zod.z.object({
|
|
13478
|
+
base64: zod.z.string(),
|
|
13479
|
+
width: zod.z.number().int(),
|
|
13480
|
+
height: zod.z.number().int()
|
|
13481
|
+
}), {
|
|
13482
|
+
kind: "query",
|
|
13483
|
+
auth: "admin"
|
|
13484
|
+
}),
|
|
13485
|
+
/**
|
|
13486
|
+
* Ask the pipeline what it sees, as a PROPOSAL.
|
|
13487
|
+
*
|
|
13488
|
+
* Runs through `pipelineRunner.runStatelessStep` on the COPIED frame, and
|
|
13489
|
+
* every box comes back as a draft with `source: 'assist'` plus the model and
|
|
13490
|
+
* score that produced it. The operator confirms, edits, adds and deletes;
|
|
13491
|
+
* nothing is stored until `saveRetrainAnnotations`.
|
|
13492
|
+
*
|
|
13493
|
+
* For packages the request is `rfdetr-package` on the ZONE CROP at 0.35 —
|
|
13494
|
+
* never the whole frame, where a package detector at that threshold proposes
|
|
13495
|
+
* furniture. A package request with no zone is REFUSED rather than widened,
|
|
13496
|
+
* because the silent widening would look like a bad model for as long as
|
|
13497
|
+
* nobody checked which rectangle it ran on.
|
|
13498
|
+
*/
|
|
13499
|
+
proposeRetrainAnnotations: require_sleep.method(zod.z.object({
|
|
13500
|
+
deviceId: zod.z.number(),
|
|
13501
|
+
trackId: zod.z.string(),
|
|
13502
|
+
frameId: zod.z.string(),
|
|
13503
|
+
subject: RetrainAssistSubjectSchema,
|
|
13504
|
+
/** Which node runs it. Absent ⇒ wherever an unowned call lands. */
|
|
13505
|
+
nodeId: zod.z.string().optional()
|
|
13506
|
+
}), RetrainAssistResultSchema, {
|
|
13507
|
+
kind: "mutation",
|
|
13508
|
+
auth: "admin"
|
|
13509
|
+
}),
|
|
13510
|
+
/** Every annotation on a track, oldest first. */
|
|
13511
|
+
listRetrainAnnotations: require_sleep.method(zod.z.object({ trackId: zod.z.string() }), zod.z.array(RetrainAnnotationSchema).readonly(), {
|
|
13512
|
+
kind: "query",
|
|
13513
|
+
auth: "admin"
|
|
13514
|
+
}),
|
|
13515
|
+
/**
|
|
13516
|
+
* Replace EVERY annotation on one frame with the supplied set.
|
|
13517
|
+
*
|
|
13518
|
+
* Whole-frame replacement, not per-box upsert: the unit of ground truth is
|
|
13519
|
+
* the frame, and "the operator deleted a box" must be the same durable
|
|
13520
|
+
* outcome as "the operator never drew it". A per-box patch would let a frame
|
|
13521
|
+
* keep a box the operator removed on a surface that only knew about the
|
|
13522
|
+
* boxes it sent.
|
|
13523
|
+
*
|
|
13524
|
+
* Refuses a macro class typed into `label` or `subLabel` — the tiers are
|
|
13525
|
+
* separate and the guard is at the WRITE, because a mixed taxonomy cannot
|
|
13526
|
+
* be un-mixed by reading it.
|
|
13527
|
+
*/
|
|
13528
|
+
saveRetrainAnnotations: require_sleep.method(zod.z.object({
|
|
13529
|
+
deviceId: zod.z.number(),
|
|
13530
|
+
trackId: zod.z.string(),
|
|
13531
|
+
frameId: zod.z.string(),
|
|
13532
|
+
annotations: zod.z.array(RetrainAnnotationDraftSchema)
|
|
13533
|
+
}), zod.z.array(RetrainAnnotationSchema).readonly(), {
|
|
13534
|
+
kind: "mutation",
|
|
13535
|
+
auth: "admin"
|
|
13536
|
+
}),
|
|
13537
|
+
/**
|
|
13538
|
+
* Finish with a track: `staging → trained`. **The only writer of that
|
|
13539
|
+
* state** — D81 shipped the column with it deliberately unreachable.
|
|
13540
|
+
*
|
|
13541
|
+
* Refuses a track the dataset holds no copies from. `trained` un-pins the
|
|
13542
|
+
* track's media, so completing without a copy is a delete order for material
|
|
13543
|
+
* nothing ever extracted anything from; that refusal IS the safety argument
|
|
13544
|
+
* of D81, expressed as a precondition.
|
|
13545
|
+
*/
|
|
13546
|
+
completeRetrainTrack: require_sleep.method(zod.z.object({
|
|
13547
|
+
deviceId: zod.z.number(),
|
|
13548
|
+
trackId: zod.z.string()
|
|
13549
|
+
}), RetrainTransitionResultSchema, {
|
|
13550
|
+
kind: "mutation",
|
|
13551
|
+
auth: "admin"
|
|
13552
|
+
}),
|
|
13553
|
+
/**
|
|
13554
|
+
* The deliberate return: `trained → staging`, for the rare case.
|
|
13555
|
+
*
|
|
13556
|
+
* The generic `setTrackFlags` toggle refuses this in both directions by
|
|
13557
|
+
* design (D81) — re-staging from a checkbox is how the same material gets
|
|
13558
|
+
* annotated twice under two ground truths. Doing it here means the operator
|
|
13559
|
+
* is looking at the annotations that already exist while they decide, and
|
|
13560
|
+
* those annotations are LEFT ALONE: "put this back" must not be a
|
|
13561
|
+
* destructive act wearing a navigational name.
|
|
13562
|
+
*/
|
|
13563
|
+
restageRetrainTrack: require_sleep.method(zod.z.object({
|
|
13564
|
+
deviceId: zod.z.number(),
|
|
13565
|
+
trackId: zod.z.string()
|
|
13566
|
+
}), RetrainTransitionResultSchema, {
|
|
13567
|
+
kind: "mutation",
|
|
13568
|
+
auth: "admin"
|
|
13569
|
+
}),
|
|
13570
|
+
/**
|
|
13571
|
+
* Where to download the ANNOTATED dataset.
|
|
13572
|
+
*
|
|
13573
|
+
* The sibling of `getTrainingExportUrl` and deliberately not the same
|
|
13574
|
+
* archive: that one streams a marked track's stored media verbatim, this one
|
|
13575
|
+
* streams the retrain COPIES plus an `annotations.json` carrying, for every
|
|
13576
|
+
* subject, the canonical full-frame box AND the geometry derived for each
|
|
13577
|
+
* model shape (letterboxed root / zone-cropped package / subject-cropped
|
|
13578
|
+
* classifier). Derived at export, never stored — one box in, three shapes
|
|
13579
|
+
* out, so two crops of the same subject can never end up in one feature
|
|
13580
|
+
* space (D52).
|
|
13581
|
+
*/
|
|
13582
|
+
getRetrainExportUrl: require_sleep.method(zod.z.object({ deviceIds: zod.z.array(zod.z.number()).optional() }), zod.z.object({ url: zod.z.string() }), {
|
|
13583
|
+
kind: "query",
|
|
13584
|
+
auth: "admin"
|
|
13585
|
+
}),
|
|
13337
13586
|
getEventMedia: require_sleep.method(zod.z.object({
|
|
13338
13587
|
eventId: zod.z.string(),
|
|
13339
13588
|
kind: MediaFileKindEnum.optional()
|
|
@@ -14160,6 +14409,22 @@ var DetailResultSchema = zod.z.object({
|
|
|
14160
14409
|
bbox: NativeCropBboxSchema.optional(),
|
|
14161
14410
|
embedding: zod.z.string().optional(),
|
|
14162
14411
|
label: zod.z.string().optional(),
|
|
14412
|
+
/**
|
|
14413
|
+
* The tier `label` occupies, copied VERBATIM from the producing step's
|
|
14414
|
+
* `StepDefinition.labelTier` (roadmap 4g). Present only when `label` is.
|
|
14415
|
+
*
|
|
14416
|
+
* It rides the wire rather than being resolved by the consumer because the
|
|
14417
|
+
* declaration lives with the step definition, which only the executing node
|
|
14418
|
+
* has: post-analysis holds no step registry, and re-deriving the tier from
|
|
14419
|
+
* `className` there would be exactly the inference this model exists to
|
|
14420
|
+
* forbid. A `label` that arrives WITHOUT this field is refused by the write
|
|
14421
|
+
* rule and logged (`label tier undeclared`) — an older runner therefore
|
|
14422
|
+
* stops enriching rather than guessing, which is why addon-pipeline is
|
|
14423
|
+
* deployed BEFORE addon-post-analysis.
|
|
14424
|
+
*/
|
|
14425
|
+
labelTier: zod.z.union([zod.z.literal(1), zod.z.literal(2)]).optional(),
|
|
14426
|
+
/** Model that produced `label` — carried into the tier's attribution. */
|
|
14427
|
+
labelModelId: zod.z.string().optional(),
|
|
14163
14428
|
alignedCropJpeg: zod.z.string().optional(),
|
|
14164
14429
|
/** Face short side (px) measured on the NATIVE crop surface. The `bbox`
|
|
14165
14430
|
* above is detection-frame px (≈6× smaller on a 4K camera) — min-face-size
|
|
@@ -22098,6 +22363,216 @@ setOverlay: require_sleep.method(zod.z.object({
|
|
|
22098
22363
|
}] }
|
|
22099
22364
|
};
|
|
22100
22365
|
//#endregion
|
|
22366
|
+
//#region src/capabilities/osd-manager.cap.ts
|
|
22367
|
+
/**
|
|
22368
|
+
* `osd-manager` — the ORCHESTRATOR over the device-scope `osd` cap.
|
|
22369
|
+
*
|
|
22370
|
+
* The `osd` cap is the firmware contract: it probes a camera's overlay
|
|
22371
|
+
* SLOTS and writes literal text into one. It has no idea WHERE that text
|
|
22372
|
+
* comes from, and it must not — a driver that grew a "show the temperature
|
|
22373
|
+
* here" feature would grow it once per vendor.
|
|
22374
|
+
*
|
|
22375
|
+
* This cap owns the other half: a per-(camera, slot) BINDING that says
|
|
22376
|
+
* which value feeds the slot, how it is formatted, and under which
|
|
22377
|
+
* conditions it is shown at all. One addon renders every binding on every
|
|
22378
|
+
* camera, so a new source costs zero driver code.
|
|
22379
|
+
*
|
|
22380
|
+
* Three deliberate choices, each with a rejected alternative:
|
|
22381
|
+
*
|
|
22382
|
+
* 1. A source is `(capName, valuePath)` over the kernel's device
|
|
22383
|
+
* runtime-state mirror — NOT a closed enum of source kinds. Every
|
|
22384
|
+
* cap-keyed slice a device publishes is bindable the day the cap
|
|
22385
|
+
* ships. The rejected alternative (one enum member per source, with
|
|
22386
|
+
* a resolver branch each) is what makes "add the humidity too" a
|
|
22387
|
+
* code change.
|
|
22388
|
+
* 2. The display gate reuses `NcConditionsSchema` verbatim — the
|
|
22389
|
+
* notification centre's condition vocabulary — rather than a parallel
|
|
22390
|
+
* model. An operator who has learned one condition editor has learned
|
|
22391
|
+
* both.
|
|
22392
|
+
* 3. Because the renderer's facts are device STATE and not a detection
|
|
22393
|
+
* record, only a SUBSET of that vocabulary can be answered here.
|
|
22394
|
+
* `setSlotBinding` REJECTS the rest at write time (see
|
|
22395
|
+
* `getConditionSupport`). It does not accept-then-fail-closed: a
|
|
22396
|
+
* condition that can never be true renders a permanently blank
|
|
22397
|
+
* overlay, and a blank overlay looks exactly like a broken camera.
|
|
22398
|
+
*/
|
|
22399
|
+
/** Where a slot's value comes from. */
|
|
22400
|
+
var OsdSourceSchema = zod.z.discriminatedUnion("kind", [
|
|
22401
|
+
zod.z.object({
|
|
22402
|
+
kind: zod.z.literal("static"),
|
|
22403
|
+
text: zod.z.string().max(64)
|
|
22404
|
+
}),
|
|
22405
|
+
zod.z.object({
|
|
22406
|
+
kind: zod.z.literal("clock"),
|
|
22407
|
+
/** Token pattern: `YYYY MM DD HH mm ss`. Everything else is literal. */
|
|
22408
|
+
pattern: zod.z.string().min(1).max(32).default("HH:mm"),
|
|
22409
|
+
/** IANA zone. Omitted = the server's zone. */
|
|
22410
|
+
timezone: zod.z.string().min(1).max(64).optional()
|
|
22411
|
+
}),
|
|
22412
|
+
zod.z.object({
|
|
22413
|
+
kind: zod.z.literal("device-state"),
|
|
22414
|
+
deviceId: zod.z.number().int().optional(),
|
|
22415
|
+
capName: zod.z.string().min(1).max(64),
|
|
22416
|
+
/** Dot path inside the slice, e.g. `detected`, `value`, `mode`. */
|
|
22417
|
+
valuePath: zod.z.string().min(1).max(64)
|
|
22418
|
+
})
|
|
22419
|
+
]);
|
|
22420
|
+
var OsdSlotBindingSchema = zod.z.object({
|
|
22421
|
+
/** Off = the manager stops driving this slot. It does NOT clear it. */
|
|
22422
|
+
enabled: zod.z.boolean().default(true),
|
|
22423
|
+
source: OsdSourceSchema,
|
|
22424
|
+
/** `${value}` and `${unit}` are substituted; every occurrence. */
|
|
22425
|
+
template: zod.z.string().max(96).default("${value}"),
|
|
22426
|
+
/** Truncate with an ellipsis past this length. Absent = no limit. */
|
|
22427
|
+
maxCharacters: zod.z.number().int().min(4).max(64).optional(),
|
|
22428
|
+
/**
|
|
22429
|
+
* Decimal places for a numeric value. `0` yields an integer — the
|
|
22430
|
+
* documented workaround for firmwares that reject `.` in overlay text.
|
|
22431
|
+
*/
|
|
22432
|
+
maxDecimals: zod.z.number().int().min(0).max(4).default(1),
|
|
22433
|
+
/** Appended via `${unit}`. The state mirror does not carry units. */
|
|
22434
|
+
unitLabel: zod.z.string().max(8).optional(),
|
|
22435
|
+
/** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
|
|
22436
|
+
valueMap: zod.z.record(zod.z.string(), zod.z.string()).optional(),
|
|
22437
|
+
/** Time windows in which the slot is shown. Absent = always. */
|
|
22438
|
+
schedule: NcScheduleSchema.optional(),
|
|
22439
|
+
/**
|
|
22440
|
+
* Display gate, in the notification centre's condition vocabulary.
|
|
22441
|
+
* Only the keys reported by `getConditionSupport` are accepted.
|
|
22442
|
+
*/
|
|
22443
|
+
conditions: NcConditionsSchema.optional(),
|
|
22444
|
+
/** Rendered when the gate is closed or the value unreadable. Empty = hide. */
|
|
22445
|
+
fallbackText: zod.z.string().max(64).default("")
|
|
22446
|
+
});
|
|
22447
|
+
/** One camera slot, as the operator sees it: firmware truth + our binding. */
|
|
22448
|
+
var OsdSlotViewSchema = zod.z.object({
|
|
22449
|
+
slotId: zod.z.string(),
|
|
22450
|
+
kind: OsdOverlayKindEnum,
|
|
22451
|
+
/** Firmware refuses text edits (a timestamp, the channel name). */
|
|
22452
|
+
readOnly: zod.z.boolean(),
|
|
22453
|
+
cameraEnabled: zod.z.boolean(),
|
|
22454
|
+
cameraText: zod.z.string().optional(),
|
|
22455
|
+
binding: OsdSlotBindingSchema.nullable()
|
|
22456
|
+
});
|
|
22457
|
+
/**
|
|
22458
|
+
* What happened to one slot on one render pass. `unchanged` exists so the
|
|
22459
|
+
* operator can tell "we are driving this and the value is steady" from
|
|
22460
|
+
* "we never got there" — and so the loop can prove it is not rewriting
|
|
22461
|
+
* identical text to the camera every tick.
|
|
22462
|
+
*/
|
|
22463
|
+
var OsdRenderOutcomeEnum = zod.z.enum([
|
|
22464
|
+
"written",
|
|
22465
|
+
"unchanged",
|
|
22466
|
+
"gated",
|
|
22467
|
+
"unreadable",
|
|
22468
|
+
"disabled",
|
|
22469
|
+
"unbound",
|
|
22470
|
+
"failed"
|
|
22471
|
+
]);
|
|
22472
|
+
var OsdRenderResultSchema = zod.z.object({
|
|
22473
|
+
slotId: zod.z.string(),
|
|
22474
|
+
outcome: OsdRenderOutcomeEnum,
|
|
22475
|
+
/** The text the slot should carry. Empty = the slot is switched off. */
|
|
22476
|
+
text: zod.z.string(),
|
|
22477
|
+
/** Why, whenever the outcome is not a plain write. Never silent. */
|
|
22478
|
+
reason: zod.z.string().optional()
|
|
22479
|
+
});
|
|
22480
|
+
var OsdSourceValueTypeEnum = zod.z.enum([
|
|
22481
|
+
"number",
|
|
22482
|
+
"boolean",
|
|
22483
|
+
"string",
|
|
22484
|
+
"enum"
|
|
22485
|
+
]);
|
|
22486
|
+
/**
|
|
22487
|
+
* One bindable value, derived from a cap's `runtimeState` schema — never
|
|
22488
|
+
* hand-listed. The editor renders from this, so a cap that ships a new
|
|
22489
|
+
* state field becomes bindable with no UI change.
|
|
22490
|
+
*/
|
|
22491
|
+
var OsdSourceOptionSchema = zod.z.object({
|
|
22492
|
+
deviceId: zod.z.number().int(),
|
|
22493
|
+
deviceName: zod.z.string(),
|
|
22494
|
+
capName: zod.z.string(),
|
|
22495
|
+
valuePath: zod.z.string(),
|
|
22496
|
+
label: zod.z.string(),
|
|
22497
|
+
valueType: OsdSourceValueTypeEnum,
|
|
22498
|
+
/** Present for `enum`; the editor offers these as `valueMap` keys. */
|
|
22499
|
+
enumValues: zod.z.array(zod.z.string()).readonly().optional()
|
|
22500
|
+
});
|
|
22501
|
+
var osdManagerCapability = {
|
|
22502
|
+
name: "osd-manager",
|
|
22503
|
+
scope: "system",
|
|
22504
|
+
mode: "singleton",
|
|
22505
|
+
methods: {
|
|
22506
|
+
/**
|
|
22507
|
+
* The camera's slots, each with its binding. `supported: false` when
|
|
22508
|
+
* the camera publishes no `osd` cap — the page then says so instead
|
|
22509
|
+
* of rendering an empty list that reads as "no overlays configured".
|
|
22510
|
+
*/
|
|
22511
|
+
getDeviceOsd: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int() }), zod.z.object({
|
|
22512
|
+
supported: zod.z.boolean(),
|
|
22513
|
+
slots: zod.z.array(OsdSlotViewSchema)
|
|
22514
|
+
}), { auth: "admin" }),
|
|
22515
|
+
/**
|
|
22516
|
+
* Every value bindable on this camera: its own state leaves first,
|
|
22517
|
+
* then every other device's. Derived from the cap definitions, so it
|
|
22518
|
+
* cannot drift from what the resolver can actually read.
|
|
22519
|
+
*/
|
|
22520
|
+
getSourceCatalog: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int() }), zod.z.object({ sources: zod.z.array(OsdSourceOptionSchema) }), { auth: "admin" }),
|
|
22521
|
+
/**
|
|
22522
|
+
* The condition ids this gate can answer, alongside the FULL notification
|
|
22523
|
+
* condition catalog. Both, in one call, on purpose: an editor that showed
|
|
22524
|
+
* the catalog without the supported set would offer conditions that
|
|
22525
|
+
* `setSlotBinding` then rejects.
|
|
22526
|
+
*/
|
|
22527
|
+
getConditionSupport: require_sleep.method(zod.z.object({}), zod.z.object({
|
|
22528
|
+
supported: zod.z.array(zod.z.string()),
|
|
22529
|
+
catalog: zod.z.array(NcConditionDescriptorSchema)
|
|
22530
|
+
}), { auth: "admin" }),
|
|
22531
|
+
/**
|
|
22532
|
+
* Persist a binding and render it immediately, returning the slot as
|
|
22533
|
+
* it now stands. Throws when the binding names an unsupported
|
|
22534
|
+
* condition, an unknown cap, or a value path that cap does not have.
|
|
22535
|
+
*/
|
|
22536
|
+
setSlotBinding: require_sleep.method(zod.z.object({
|
|
22537
|
+
deviceId: zod.z.number().int(),
|
|
22538
|
+
slotId: zod.z.string().min(1),
|
|
22539
|
+
binding: OsdSlotBindingSchema
|
|
22540
|
+
}), zod.z.object({
|
|
22541
|
+
slot: OsdSlotViewSchema,
|
|
22542
|
+
render: OsdRenderResultSchema
|
|
22543
|
+
}), {
|
|
22544
|
+
kind: "mutation",
|
|
22545
|
+
auth: "admin"
|
|
22546
|
+
}),
|
|
22547
|
+
/** Forget the binding. The slot keeps whatever text it last carried. */
|
|
22548
|
+
clearSlotBinding: require_sleep.method(zod.z.object({
|
|
22549
|
+
deviceId: zod.z.number().int(),
|
|
22550
|
+
slotId: zod.z.string().min(1)
|
|
22551
|
+
}), zod.z.object({ success: zod.z.literal(true) }), {
|
|
22552
|
+
kind: "mutation",
|
|
22553
|
+
auth: "admin"
|
|
22554
|
+
}),
|
|
22555
|
+
/**
|
|
22556
|
+
* Render without writing. `binding` overrides the stored one so an
|
|
22557
|
+
* editor can preview an unsaved change. Mutation kind only to carry
|
|
22558
|
+
* the binding object safely; no side effects.
|
|
22559
|
+
*/
|
|
22560
|
+
previewSlot: require_sleep.method(zod.z.object({
|
|
22561
|
+
deviceId: zod.z.number().int(),
|
|
22562
|
+
slotId: zod.z.string().min(1),
|
|
22563
|
+
binding: OsdSlotBindingSchema.optional()
|
|
22564
|
+
}), OsdRenderResultSchema, {
|
|
22565
|
+
kind: "mutation",
|
|
22566
|
+
auth: "admin"
|
|
22567
|
+
}),
|
|
22568
|
+
/** Force a render pass over one camera now, instead of at the next tick. */
|
|
22569
|
+
renderDevice: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int() }), zod.z.object({ results: zod.z.array(OsdRenderResultSchema) }), {
|
|
22570
|
+
kind: "mutation",
|
|
22571
|
+
auth: "admin"
|
|
22572
|
+
})
|
|
22573
|
+
}
|
|
22574
|
+
};
|
|
22575
|
+
//#endregion
|
|
22101
22576
|
//#region src/capabilities/pet-feeder.cap.ts
|
|
22102
22577
|
/**
|
|
22103
22578
|
* PetKit pet-feeder cap. Models the control + telemetry surface of a
|
|
@@ -28185,6 +28660,7 @@ var CAPABILITY_NAMES = {
|
|
|
28185
28660
|
numericSensor: "numeric-sensor",
|
|
28186
28661
|
oauthIntegration: "oauth-integration",
|
|
28187
28662
|
osd: "osd",
|
|
28663
|
+
osdManager: "osd-manager",
|
|
28188
28664
|
petFeeder: "pet-feeder",
|
|
28189
28665
|
pipelineAnalytics: "pipeline-analytics",
|
|
28190
28666
|
pipelineExecutor: "pipeline-executor",
|
|
@@ -28616,6 +29092,10 @@ var CAPABILITY_ROUTER_KEYS = [
|
|
|
28616
29092
|
key: "osd",
|
|
28617
29093
|
name: "osd"
|
|
28618
29094
|
},
|
|
29095
|
+
{
|
|
29096
|
+
key: "osdManager",
|
|
29097
|
+
name: "osd-manager"
|
|
29098
|
+
},
|
|
28619
29099
|
{
|
|
28620
29100
|
key: "petFeeder",
|
|
28621
29101
|
name: "pet-feeder"
|
|
@@ -28929,6 +29409,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
|
|
|
28929
29409
|
numericSensorCapability,
|
|
28930
29410
|
oauthIntegrationCapability,
|
|
28931
29411
|
osdCapability,
|
|
29412
|
+
osdManagerCapability,
|
|
28932
29413
|
petFeederCapability,
|
|
28933
29414
|
pipelineAnalyticsCapability,
|
|
28934
29415
|
pipelineExecutorCapability,
|
|
@@ -31847,6 +32328,48 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
31847
32328
|
addonId: null,
|
|
31848
32329
|
access: "create"
|
|
31849
32330
|
},
|
|
32331
|
+
"osdManager.clearSlotBinding": {
|
|
32332
|
+
capName: "osd-manager",
|
|
32333
|
+
capScope: "system",
|
|
32334
|
+
addonId: null,
|
|
32335
|
+
access: "delete"
|
|
32336
|
+
},
|
|
32337
|
+
"osdManager.getConditionSupport": {
|
|
32338
|
+
capName: "osd-manager",
|
|
32339
|
+
capScope: "system",
|
|
32340
|
+
addonId: null,
|
|
32341
|
+
access: "view"
|
|
32342
|
+
},
|
|
32343
|
+
"osdManager.getDeviceOsd": {
|
|
32344
|
+
capName: "osd-manager",
|
|
32345
|
+
capScope: "system",
|
|
32346
|
+
addonId: null,
|
|
32347
|
+
access: "view"
|
|
32348
|
+
},
|
|
32349
|
+
"osdManager.getSourceCatalog": {
|
|
32350
|
+
capName: "osd-manager",
|
|
32351
|
+
capScope: "system",
|
|
32352
|
+
addonId: null,
|
|
32353
|
+
access: "view"
|
|
32354
|
+
},
|
|
32355
|
+
"osdManager.previewSlot": {
|
|
32356
|
+
capName: "osd-manager",
|
|
32357
|
+
capScope: "system",
|
|
32358
|
+
addonId: null,
|
|
32359
|
+
access: "create"
|
|
32360
|
+
},
|
|
32361
|
+
"osdManager.renderDevice": {
|
|
32362
|
+
capName: "osd-manager",
|
|
32363
|
+
capScope: "system",
|
|
32364
|
+
addonId: null,
|
|
32365
|
+
access: "create"
|
|
32366
|
+
},
|
|
32367
|
+
"osdManager.setSlotBinding": {
|
|
32368
|
+
capName: "osd-manager",
|
|
32369
|
+
capScope: "system",
|
|
32370
|
+
addonId: null,
|
|
32371
|
+
access: "create"
|
|
32372
|
+
},
|
|
31850
32373
|
"petFeeder.callPet": {
|
|
31851
32374
|
capName: "pet-feeder",
|
|
31852
32375
|
capScope: "device",
|
|
@@ -31919,6 +32442,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
31919
32442
|
addonId: null,
|
|
31920
32443
|
access: "delete"
|
|
31921
32444
|
},
|
|
32445
|
+
"pipelineAnalytics.completeRetrainTrack": {
|
|
32446
|
+
capName: "pipeline-analytics",
|
|
32447
|
+
capScope: "device",
|
|
32448
|
+
addonId: null,
|
|
32449
|
+
access: "create"
|
|
32450
|
+
},
|
|
31922
32451
|
"pipelineAnalytics.deleteDeviceEvents": {
|
|
31923
32452
|
capName: "pipeline-analytics",
|
|
31924
32453
|
capScope: "device",
|
|
@@ -31931,6 +32460,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
31931
32460
|
addonId: null,
|
|
31932
32461
|
access: "delete"
|
|
31933
32462
|
},
|
|
32463
|
+
"pipelineAnalytics.deselectRetrainFrame": {
|
|
32464
|
+
capName: "pipeline-analytics",
|
|
32465
|
+
capScope: "device",
|
|
32466
|
+
addonId: null,
|
|
32467
|
+
access: "create"
|
|
32468
|
+
},
|
|
31934
32469
|
"pipelineAnalytics.getActiveTracks": {
|
|
31935
32470
|
capName: "pipeline-analytics",
|
|
31936
32471
|
capScope: "device",
|
|
@@ -31991,6 +32526,18 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
31991
32526
|
addonId: null,
|
|
31992
32527
|
access: "view"
|
|
31993
32528
|
},
|
|
32529
|
+
"pipelineAnalytics.getRetrainExportUrl": {
|
|
32530
|
+
capName: "pipeline-analytics",
|
|
32531
|
+
capScope: "device",
|
|
32532
|
+
addonId: null,
|
|
32533
|
+
access: "view"
|
|
32534
|
+
},
|
|
32535
|
+
"pipelineAnalytics.getRetrainFrameImage": {
|
|
32536
|
+
capName: "pipeline-analytics",
|
|
32537
|
+
capScope: "device",
|
|
32538
|
+
addonId: null,
|
|
32539
|
+
access: "view"
|
|
32540
|
+
},
|
|
31994
32541
|
"pipelineAnalytics.getSensorEvents": {
|
|
31995
32542
|
capName: "pipeline-analytics",
|
|
31996
32543
|
capScope: "device",
|
|
@@ -32009,6 +32556,18 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
32009
32556
|
addonId: null,
|
|
32010
32557
|
access: "view"
|
|
32011
32558
|
},
|
|
32559
|
+
"pipelineAnalytics.getTrainingExportSummary": {
|
|
32560
|
+
capName: "pipeline-analytics",
|
|
32561
|
+
capScope: "device",
|
|
32562
|
+
addonId: null,
|
|
32563
|
+
access: "view"
|
|
32564
|
+
},
|
|
32565
|
+
"pipelineAnalytics.getTrainingExportUrl": {
|
|
32566
|
+
capName: "pipeline-analytics",
|
|
32567
|
+
capScope: "device",
|
|
32568
|
+
addonId: null,
|
|
32569
|
+
access: "view"
|
|
32570
|
+
},
|
|
32012
32571
|
"pipelineAnalytics.listEventKinds": {
|
|
32013
32572
|
capName: "pipeline-analytics",
|
|
32014
32573
|
capScope: "device",
|
|
@@ -32033,6 +32592,24 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
32033
32592
|
addonId: null,
|
|
32034
32593
|
access: "view"
|
|
32035
32594
|
},
|
|
32595
|
+
"pipelineAnalytics.listRetrainAnnotations": {
|
|
32596
|
+
capName: "pipeline-analytics",
|
|
32597
|
+
capScope: "device",
|
|
32598
|
+
addonId: null,
|
|
32599
|
+
access: "view"
|
|
32600
|
+
},
|
|
32601
|
+
"pipelineAnalytics.listRetrainFrames": {
|
|
32602
|
+
capName: "pipeline-analytics",
|
|
32603
|
+
capScope: "device",
|
|
32604
|
+
addonId: null,
|
|
32605
|
+
access: "view"
|
|
32606
|
+
},
|
|
32607
|
+
"pipelineAnalytics.listRetrainStaging": {
|
|
32608
|
+
capName: "pipeline-analytics",
|
|
32609
|
+
capScope: "device",
|
|
32610
|
+
addonId: null,
|
|
32611
|
+
access: "view"
|
|
32612
|
+
},
|
|
32036
32613
|
"pipelineAnalytics.listTrackMedia": {
|
|
32037
32614
|
capName: "pipeline-analytics",
|
|
32038
32615
|
capScope: "device",
|
|
@@ -32045,6 +32622,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
32045
32622
|
addonId: null,
|
|
32046
32623
|
access: "view"
|
|
32047
32624
|
},
|
|
32625
|
+
"pipelineAnalytics.proposeRetrainAnnotations": {
|
|
32626
|
+
capName: "pipeline-analytics",
|
|
32627
|
+
capScope: "device",
|
|
32628
|
+
addonId: null,
|
|
32629
|
+
access: "create"
|
|
32630
|
+
},
|
|
32048
32631
|
"pipelineAnalytics.pruneEvents": {
|
|
32049
32632
|
capName: "pipeline-analytics",
|
|
32050
32633
|
capScope: "device",
|
|
@@ -32075,12 +32658,30 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
32075
32658
|
addonId: null,
|
|
32076
32659
|
access: "create"
|
|
32077
32660
|
},
|
|
32661
|
+
"pipelineAnalytics.restageRetrainTrack": {
|
|
32662
|
+
capName: "pipeline-analytics",
|
|
32663
|
+
capScope: "device",
|
|
32664
|
+
addonId: null,
|
|
32665
|
+
access: "create"
|
|
32666
|
+
},
|
|
32667
|
+
"pipelineAnalytics.saveRetrainAnnotations": {
|
|
32668
|
+
capName: "pipeline-analytics",
|
|
32669
|
+
capScope: "device",
|
|
32670
|
+
addonId: null,
|
|
32671
|
+
access: "create"
|
|
32672
|
+
},
|
|
32078
32673
|
"pipelineAnalytics.searchObjectEvents": {
|
|
32079
32674
|
capName: "pipeline-analytics",
|
|
32080
32675
|
capScope: "device",
|
|
32081
32676
|
addonId: null,
|
|
32082
32677
|
access: "view"
|
|
32083
32678
|
},
|
|
32679
|
+
"pipelineAnalytics.selectRetrainFrames": {
|
|
32680
|
+
capName: "pipeline-analytics",
|
|
32681
|
+
capScope: "device",
|
|
32682
|
+
addonId: null,
|
|
32683
|
+
access: "create"
|
|
32684
|
+
},
|
|
32084
32685
|
"pipelineAnalytics.setTrackFlags": {
|
|
32085
32686
|
capName: "pipeline-analytics",
|
|
32086
32687
|
capScope: "device",
|
|
@@ -33485,6 +34086,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
33485
34086
|
addonId: null,
|
|
33486
34087
|
access: "view"
|
|
33487
34088
|
},
|
|
34089
|
+
"streamBroker.getDeviceAudioMute": {
|
|
34090
|
+
capName: "stream-broker",
|
|
34091
|
+
capScope: "system",
|
|
34092
|
+
addonId: null,
|
|
34093
|
+
access: "view"
|
|
34094
|
+
},
|
|
33488
34095
|
"streamBroker.getPreBufferInfo": {
|
|
33489
34096
|
capName: "stream-broker",
|
|
33490
34097
|
capScope: "system",
|
|
@@ -33605,6 +34212,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
33605
34212
|
addonId: null,
|
|
33606
34213
|
access: "create"
|
|
33607
34214
|
},
|
|
34215
|
+
"streamBroker.setDeviceAudioMute": {
|
|
34216
|
+
capName: "stream-broker",
|
|
34217
|
+
capScope: "system",
|
|
34218
|
+
addonId: null,
|
|
34219
|
+
access: "create"
|
|
34220
|
+
},
|
|
33608
34221
|
"streamBroker.setPreBufferDuration": {
|
|
33609
34222
|
capName: "stream-broker",
|
|
33610
34223
|
capScope: "system",
|
|
@@ -34320,6 +34933,7 @@ var KNOWN_CAP_NAMES = [
|
|
|
34320
34933
|
"notifier",
|
|
34321
34934
|
"oauth-integration",
|
|
34322
34935
|
"osd",
|
|
34936
|
+
"osd-manager",
|
|
34323
34937
|
"pet-feeder",
|
|
34324
34938
|
"pipeline-analytics",
|
|
34325
34939
|
"pipeline-executor",
|
|
@@ -34467,6 +35081,7 @@ var SYSTEM_CAP_NAMES = [
|
|
|
34467
35081
|
"notification-output",
|
|
34468
35082
|
"notification-rules",
|
|
34469
35083
|
"oauth-integration",
|
|
35084
|
+
"osd-manager",
|
|
34470
35085
|
"pipeline-executor",
|
|
34471
35086
|
"pipeline-orchestrator",
|
|
34472
35087
|
"pipeline-runner",
|
|
@@ -34925,6 +35540,7 @@ function createSystemProxy(api) {
|
|
|
34925
35540
|
deleteTarget: (input) => dispatch("notificationOutput", "deleteTarget", "mutation", input),
|
|
34926
35541
|
setTargetEnabled: (input) => dispatch("notificationOutput", "setTargetEnabled", "mutation", input)
|
|
34927
35542
|
},
|
|
35543
|
+
osdManager: { getConditionSupport: (input) => dispatch("osdManager", "getConditionSupport", "query", input) },
|
|
34928
35544
|
pipelineExecutor: {
|
|
34929
35545
|
getAvailableEngines: (input) => dispatch("pipelineExecutor", "getAvailableEngines", "query", input),
|
|
34930
35546
|
getSelectedEngine: (input) => dispatch("pipelineExecutor", "getSelectedEngine", "query", input),
|
|
@@ -35466,6 +36082,68 @@ function prepareNotification(caps, n) {
|
|
|
35466
36082
|
};
|
|
35467
36083
|
}
|
|
35468
36084
|
//#endregion
|
|
36085
|
+
//#region src/notification/schedule.ts
|
|
36086
|
+
var WEEKDAY_TO_DAY = {
|
|
36087
|
+
Sun: 0,
|
|
36088
|
+
Mon: 1,
|
|
36089
|
+
Tue: 2,
|
|
36090
|
+
Wed: 3,
|
|
36091
|
+
Thu: 4,
|
|
36092
|
+
Fri: 5,
|
|
36093
|
+
Sat: 6
|
|
36094
|
+
};
|
|
36095
|
+
/** Resolve (weekday, minute-of-day) of `atMs` in the schedule's timezone.
|
|
36096
|
+
* An invalid/unknown IANA name falls back to the host timezone. */
|
|
36097
|
+
function localDayMinute(atMs, timezone) {
|
|
36098
|
+
const d = new Date(atMs);
|
|
36099
|
+
if (timezone !== void 0) try {
|
|
36100
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
36101
|
+
timeZone: timezone,
|
|
36102
|
+
weekday: "short",
|
|
36103
|
+
hour: "numeric",
|
|
36104
|
+
minute: "numeric",
|
|
36105
|
+
hourCycle: "h23"
|
|
36106
|
+
}).formatToParts(d);
|
|
36107
|
+
let weekday;
|
|
36108
|
+
let hour;
|
|
36109
|
+
let minute;
|
|
36110
|
+
for (const p of parts) if (p.type === "weekday") weekday = p.value;
|
|
36111
|
+
else if (p.type === "hour") hour = Number(p.value);
|
|
36112
|
+
else if (p.type === "minute") minute = Number(p.value);
|
|
36113
|
+
const day = weekday !== void 0 ? WEEKDAY_TO_DAY[weekday] : void 0;
|
|
36114
|
+
if (day !== void 0 && hour !== void 0 && minute !== void 0) return {
|
|
36115
|
+
day,
|
|
36116
|
+
minute: hour * 60 + minute
|
|
36117
|
+
};
|
|
36118
|
+
} catch {}
|
|
36119
|
+
return {
|
|
36120
|
+
day: d.getDay(),
|
|
36121
|
+
minute: d.getHours() * 60 + d.getMinutes()
|
|
36122
|
+
};
|
|
36123
|
+
}
|
|
36124
|
+
/**
|
|
36125
|
+
* Is the schedule active at `atMs`? No schedule = always active. Windows
|
|
36126
|
+
* are OR'd; a window with `startMinute > endMinute` crosses midnight (it
|
|
36127
|
+
* starts on a listed day and spills into the next). `invert` flips the
|
|
36128
|
+
* result (active OUTSIDE the windows).
|
|
36129
|
+
*/
|
|
36130
|
+
function isScheduleActive(schedule, atMs) {
|
|
36131
|
+
if (schedule === void 0) return true;
|
|
36132
|
+
const { day, minute } = localDayMinute(atMs, schedule.timezone);
|
|
36133
|
+
const prevDay = (day + 6) % 7;
|
|
36134
|
+
let inside = false;
|
|
36135
|
+
for (const w of schedule.windows) if (w.startMinute <= w.endMinute) {
|
|
36136
|
+
if (w.days.includes(day) && minute >= w.startMinute && minute < w.endMinute) {
|
|
36137
|
+
inside = true;
|
|
36138
|
+
break;
|
|
36139
|
+
}
|
|
36140
|
+
} else if (w.days.includes(day) && minute >= w.startMinute || w.days.includes(prevDay) && minute < w.endMinute) {
|
|
36141
|
+
inside = true;
|
|
36142
|
+
break;
|
|
36143
|
+
}
|
|
36144
|
+
return schedule.invert === true ? !inside : inside;
|
|
36145
|
+
}
|
|
36146
|
+
//#endregion
|
|
35469
36147
|
//#region src/notification/timelapse-rule.ts
|
|
35470
36148
|
/**
|
|
35471
36149
|
* TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
|
|
@@ -37326,7 +38004,7 @@ exports.APPLE_SA_TO_MACRO = APPLE_SA_TO_MACRO;
|
|
|
37326
38004
|
exports.AUDIO_ANALYSIS_CAP_NAME = AUDIO_ANALYSIS_CAP_NAME;
|
|
37327
38005
|
exports.AUDIO_BACKEND_CHOICES = AUDIO_BACKEND_CHOICES;
|
|
37328
38006
|
exports.AUDIO_MACRO_LABELS = AUDIO_MACRO_LABELS;
|
|
37329
|
-
exports.AUDIO_PRESETS = AUDIO_PRESETS;
|
|
38007
|
+
exports.AUDIO_PRESETS = require_fmp4_box_splitter.AUDIO_PRESETS;
|
|
37330
38008
|
exports.AccessoriesStatusSchema = AccessoriesStatusSchema;
|
|
37331
38009
|
exports.AccessoryKind = AccessoryKind;
|
|
37332
38010
|
exports.AddBrokerInputSchema = AddBrokerInputSchema;
|
|
@@ -37599,6 +38277,7 @@ exports.FanDirectionSchema = FanDirectionSchema;
|
|
|
37599
38277
|
exports.FeatureManifestSchema = FeatureManifestSchema;
|
|
37600
38278
|
exports.FeatureProbeStatusSchema = FeatureProbeStatusSchema;
|
|
37601
38279
|
exports.FloodStatusSchema = FloodStatusSchema;
|
|
38280
|
+
exports.Fmp4BoxSplitter = require_fmp4_box_splitter.Fmp4BoxSplitter;
|
|
37602
38281
|
exports.FrameHandleFormatSchema = require_sleep.FrameHandleFormatSchema;
|
|
37603
38282
|
exports.FrameHandleSchema = require_sleep.FrameHandleSchema;
|
|
37604
38283
|
exports.FrameInputSchema = FrameInputSchema;
|
|
@@ -37634,7 +38313,9 @@ exports.IntercomStatusSchema = IntercomStatusSchema;
|
|
|
37634
38313
|
exports.KNOWN_CAP_NAMES = KNOWN_CAP_NAMES;
|
|
37635
38314
|
exports.KeyEventSchema = KeyEventSchema;
|
|
37636
38315
|
exports.LOG_LEVEL_RANK = LOG_LEVEL_RANK;
|
|
38316
|
+
exports.LabelAttributionSchema = LabelAttributionSchema;
|
|
37637
38317
|
exports.LabelDefinitionSchema = LabelDefinitionSchema;
|
|
38318
|
+
exports.LabelTierSchema = LabelTierSchema;
|
|
37638
38319
|
exports.LawnMowerActivitySchema = LawnMowerActivitySchema;
|
|
37639
38320
|
exports.LawnMowerControlStatusSchema = LawnMowerControlStatusSchema;
|
|
37640
38321
|
exports.LinkedDeviceSchema = LinkedDeviceSchema;
|
|
@@ -37805,6 +38486,13 @@ exports.OsdOverlayKindEnum = OsdOverlayKindEnum;
|
|
|
37805
38486
|
exports.OsdOverlayPatchSchema = OsdOverlayPatchSchema;
|
|
37806
38487
|
exports.OsdOverlaySchema = OsdOverlaySchema;
|
|
37807
38488
|
exports.OsdPositionEnum = OsdPositionEnum;
|
|
38489
|
+
exports.OsdRenderOutcomeEnum = OsdRenderOutcomeEnum;
|
|
38490
|
+
exports.OsdRenderResultSchema = OsdRenderResultSchema;
|
|
38491
|
+
exports.OsdSlotBindingSchema = OsdSlotBindingSchema;
|
|
38492
|
+
exports.OsdSlotViewSchema = OsdSlotViewSchema;
|
|
38493
|
+
exports.OsdSourceOptionSchema = OsdSourceOptionSchema;
|
|
38494
|
+
exports.OsdSourceSchema = OsdSourceSchema;
|
|
38495
|
+
exports.OsdSourceValueTypeEnum = OsdSourceValueTypeEnum;
|
|
37808
38496
|
exports.OsdStatusSchema = OsdStatusSchema;
|
|
37809
38497
|
exports.PET_FEEDER_MANUAL_FEED_MAX = PET_FEEDER_MANUAL_FEED_MAX;
|
|
37810
38498
|
exports.PET_FEEDER_MANUAL_FEED_MIN = PET_FEEDER_MANUAL_FEED_MIN;
|
|
@@ -37893,6 +38581,21 @@ exports.RelocateJobStateSchema = RelocateJobStateSchema;
|
|
|
37893
38581
|
exports.RelocateMediaInputSchema = RelocateMediaInputSchema;
|
|
37894
38582
|
exports.RenderedAsSchema = RenderedAsSchema;
|
|
37895
38583
|
exports.ReportMotionInputSchema = ReportMotionInputSchema;
|
|
38584
|
+
exports.RetrainAnnotationDraftSchema = RetrainAnnotationDraftSchema;
|
|
38585
|
+
exports.RetrainAnnotationKindSchema = RetrainAnnotationKindSchema;
|
|
38586
|
+
exports.RetrainAnnotationSchema = RetrainAnnotationSchema;
|
|
38587
|
+
exports.RetrainAnnotationSourceSchema = RetrainAnnotationSourceSchema;
|
|
38588
|
+
exports.RetrainAssistResultSchema = RetrainAssistResultSchema;
|
|
38589
|
+
exports.RetrainAssistSubjectSchema = RetrainAssistSubjectSchema;
|
|
38590
|
+
exports.RetrainCopyRefusalSchema = RetrainCopyRefusalSchema;
|
|
38591
|
+
exports.RetrainFrameCandidateSchema = RetrainFrameCandidateSchema;
|
|
38592
|
+
exports.RetrainFrameListSchema = RetrainFrameListSchema;
|
|
38593
|
+
exports.RetrainFrameSchema = RetrainFrameSchema;
|
|
38594
|
+
exports.RetrainFrameSelectionSchema = RetrainFrameSelectionSchema;
|
|
38595
|
+
exports.RetrainMacroClassSchema = RetrainMacroClassSchema;
|
|
38596
|
+
exports.RetrainStatusSchema = RetrainStatusSchema;
|
|
38597
|
+
exports.RetrainTrackSchema = RetrainTrackSchema;
|
|
38598
|
+
exports.RetrainTransitionResultSchema = RetrainTransitionResultSchema;
|
|
37896
38599
|
exports.RingBuffer = RingBuffer;
|
|
37897
38600
|
exports.RtpSourceSchema = RtpSourceSchema;
|
|
37898
38601
|
exports.RtspRestreamEntrySchema = RtspRestreamEntrySchema;
|
|
@@ -38015,6 +38718,8 @@ exports.TrackSourceSchema = TrackSourceSchema;
|
|
|
38015
38718
|
exports.TrackStateSchema = TrackStateSchema;
|
|
38016
38719
|
exports.TrackZoneFilterSchema = TrackZoneFilterSchema;
|
|
38017
38720
|
exports.TrackedDetectionSchema = TrackedDetectionSchema;
|
|
38721
|
+
exports.TrainingExportDeviceTotalsSchema = TrainingExportDeviceTotalsSchema;
|
|
38722
|
+
exports.TrainingExportSummarySchema = TrainingExportSummarySchema;
|
|
38018
38723
|
exports.TurnServerSchema = TurnServerSchema;
|
|
38019
38724
|
exports.UNIT_TABLE = UNIT_TABLE;
|
|
38020
38725
|
exports.UnifiedBrokerInfoSchema = BrokerInfoSchema$1;
|
|
@@ -38094,7 +38799,7 @@ exports.audioAnalysisCapability = audioAnalysisCapability;
|
|
|
38094
38799
|
exports.audioAnalyzerCapability = audioAnalyzerCapability;
|
|
38095
38800
|
exports.audioCodecCapability = audioCodecCapability;
|
|
38096
38801
|
exports.audioMetricsCapability = audioMetricsCapability;
|
|
38097
|
-
exports.audioPlanFromEncodeProfile = audioPlanFromEncodeProfile;
|
|
38802
|
+
exports.audioPlanFromEncodeProfile = require_fmp4_box_splitter.audioPlanFromEncodeProfile;
|
|
38098
38803
|
exports.authProviderCapability = authProviderCapability;
|
|
38099
38804
|
exports.autoAssignProfiles = autoAssignProfiles;
|
|
38100
38805
|
exports.automationControlCapability = automationControlCapability;
|
|
@@ -38107,14 +38812,14 @@ exports.bindAddonActions = bindAddonActions;
|
|
|
38107
38812
|
exports.brightnessCapability = brightnessCapability;
|
|
38108
38813
|
exports.brokerCapability = brokerCapability;
|
|
38109
38814
|
exports.buildAddonRouteProvider = buildAddonRouteProvider;
|
|
38110
|
-
exports.buildAudioArgs = buildAudioArgs;
|
|
38815
|
+
exports.buildAudioArgs = require_fmp4_box_splitter.buildAudioArgs;
|
|
38111
38816
|
exports.buildEventKindDescriptor = buildEventKindDescriptor;
|
|
38112
|
-
exports.buildFfmpegArgs = buildFfmpegArgs;
|
|
38113
|
-
exports.buildInputArgs = buildInputArgs;
|
|
38817
|
+
exports.buildFfmpegArgs = require_fmp4_box_splitter.buildFfmpegArgs;
|
|
38818
|
+
exports.buildInputArgs = require_fmp4_box_splitter.buildInputArgs;
|
|
38114
38819
|
exports.buildModelVariantGroups = buildModelVariantGroups;
|
|
38115
38820
|
exports.buildNcTaxonomy = buildNcTaxonomy;
|
|
38116
38821
|
exports.buildStreamParamsConfigSchema = buildStreamParamsConfigSchema;
|
|
38117
|
-
exports.buildVideoArgs = buildVideoArgs;
|
|
38822
|
+
exports.buildVideoArgs = require_fmp4_box_splitter.buildVideoArgs;
|
|
38118
38823
|
exports.buttonCapability = buttonCapability;
|
|
38119
38824
|
exports.cameraCredentialsCapability = cameraCredentialsCapability;
|
|
38120
38825
|
exports.cameraPipelineConfigCapability = cameraPipelineConfigCapability;
|
|
@@ -38219,7 +38924,7 @@ exports.imageCapability = imageCapability;
|
|
|
38219
38924
|
exports.imageSettingsCapability = imageSettingsCapability;
|
|
38220
38925
|
exports.integrationsCapability = integrationsCapability;
|
|
38221
38926
|
exports.intercomCapability = intercomCapability;
|
|
38222
|
-
exports.invocationFromEncodeProfile = invocationFromEncodeProfile;
|
|
38927
|
+
exports.invocationFromEncodeProfile = require_fmp4_box_splitter.invocationFromEncodeProfile;
|
|
38223
38928
|
exports.isAgentOnlyPlacement = isAgentOnlyPlacement;
|
|
38224
38929
|
exports.isArrayOutputSchema = isArrayOutputSchema;
|
|
38225
38930
|
exports.isBaseConditionKey = isBaseConditionKey;
|
|
@@ -38231,7 +38936,8 @@ exports.isEvent = require_sleep.isEvent;
|
|
|
38231
38936
|
exports.isNode = isNode;
|
|
38232
38937
|
exports.isObjectInput = isObjectInput;
|
|
38233
38938
|
exports.isSameAddonId = isSameAddonId;
|
|
38234
|
-
exports.
|
|
38939
|
+
exports.isScheduleActive = isScheduleActive;
|
|
38940
|
+
exports.isSoftwareDecode = require_fmp4_box_splitter.isSoftwareDecode;
|
|
38235
38941
|
exports.isVoidInput = isVoidInput;
|
|
38236
38942
|
exports.jobKindSchema = jobKindSchema;
|
|
38237
38943
|
exports.kebabToCamel = kebabToCamel;
|
|
@@ -38246,7 +38952,7 @@ exports.llmRuntimeCapability = llmRuntimeCapability;
|
|
|
38246
38952
|
exports.localNetworkCapability = localNetworkCapability;
|
|
38247
38953
|
exports.locationSimilarity = locationSimilarity;
|
|
38248
38954
|
exports.lockControlCapability = lockControlCapability;
|
|
38249
|
-
exports.logBannerArgs = logBannerArgs;
|
|
38955
|
+
exports.logBannerArgs = require_fmp4_box_splitter.logBannerArgs;
|
|
38250
38956
|
exports.logDestinationCapability = logDestinationCapability;
|
|
38251
38957
|
exports.logLevelAtMost = logLevelAtMost;
|
|
38252
38958
|
exports.loginMethodCapability = loginMethodCapability;
|
|
@@ -38284,6 +38990,7 @@ exports.numericSensorCapability = numericSensorCapability;
|
|
|
38284
38990
|
exports.oauthIntegrationCapability = oauthIntegrationCapability;
|
|
38285
38991
|
exports.objectInputDeclaresAddonId = objectInputDeclaresAddonId;
|
|
38286
38992
|
exports.osdCapability = osdCapability;
|
|
38993
|
+
exports.osdManagerCapability = osdManagerCapability;
|
|
38287
38994
|
exports.parseCameraStreamConfig = parseCameraStreamConfig;
|
|
38288
38995
|
exports.parseExpression = parseExpression;
|
|
38289
38996
|
exports.parseJsonArray = require_sleep.parseJsonArray;
|
|
@@ -38296,7 +39003,7 @@ exports.pickAccessoryControl = pickAccessoryControl;
|
|
|
38296
39003
|
exports.pickDetailCropConvention = pickDetailCropConvention;
|
|
38297
39004
|
exports.pickNativeLeaseOverride = pickNativeLeaseOverride;
|
|
38298
39005
|
exports.pickPreferredRtspEntry = pickPreferredRtspEntry;
|
|
38299
|
-
exports.pickVideoEncoder = pickVideoEncoder;
|
|
39006
|
+
exports.pickVideoEncoder = require_fmp4_box_splitter.pickVideoEncoder;
|
|
38300
39007
|
exports.pickerForCondition = pickerForCondition;
|
|
38301
39008
|
exports.pipelineAnalyticsCapability = pipelineAnalyticsCapability;
|
|
38302
39009
|
exports.pipelineExecutorCapability = pipelineExecutorCapability;
|