@camstack/addon-decoder-ffmpeg 1.1.14 → 1.1.16

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.
Files changed (3) hide show
  1. package/dist/index.js +202 -13
  2. package/dist/index.mjs +202 -13
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ Object.defineProperties(exports, {
4
4
  });
5
5
  let node_crypto = require("node:crypto");
6
6
  let node_child_process = require("node:child_process");
7
- //#region ../types/dist/event-category-CFZs3jI4.mjs
7
+ //#region ../types/dist/event-category-H4AVePnn.mjs
8
8
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
9
9
  EventCategory["SystemBoot"] = "system.boot";
10
10
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -154,6 +154,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
154
154
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
155
155
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
156
156
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
157
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
158
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
159
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
157
160
  EventCategory["DetectionEvent"] = "detection.event";
158
161
  EventCategory["SessionTrackNew"] = "session.track.new";
159
162
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -7235,6 +7238,24 @@ var RecordingRetentionSchema = object({
7235
7238
  maxSizeGb: number().min(0).optional()
7236
7239
  });
7237
7240
  /**
7241
+ * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7242
+ * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7243
+ * previews at. Five graduated steps; absent on a config = `standard` (the
7244
+ * shipped default, matching `sheet-geometry`/`sheet-composer`).
7245
+ *
7246
+ * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7247
+ * Each window's index sidecar carries its own tile dims, so a camera whose
7248
+ * preset changed over time renders every historical window at the dims it was
7249
+ * written with.
7250
+ */
7251
+ var ScrubThumbnailPresetSchema = _enum([
7252
+ "minimal",
7253
+ "low",
7254
+ "standard",
7255
+ "high",
7256
+ "max"
7257
+ ]);
7258
+ /**
7238
7259
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7239
7260
  *
7240
7261
  * `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
@@ -7271,7 +7292,14 @@ var RecordingConfigSchema = object({
7271
7292
  * derived into bands once via `migrateConfigToBands`.
7272
7293
  */
7273
7294
  bands: array(RecordingBandSchema).optional(),
7274
- retention: RecordingRetentionSchema.optional()
7295
+ retention: RecordingRetentionSchema.optional(),
7296
+ /**
7297
+ * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7298
+ * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7299
+ * windows only — existing sheets are immutable, and each window's index
7300
+ * carries its own tile dims so mixed-preset history renders correctly.
7301
+ */
7302
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7275
7303
  });
7276
7304
  /**
7277
7305
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -9830,7 +9858,7 @@ object({
9830
9858
  deviceId: number(),
9831
9859
  timestamp: number()
9832
9860
  });
9833
- DeviceType.Button;
9861
+ DeviceType.Button, DeviceType.Camera;
9834
9862
  /**
9835
9863
  * Enum-state sensor — a string value picked from a finite option set.
9836
9864
  * Drives HA `sensor` entries with `state_class: enum` (HVAC action
@@ -14330,6 +14358,14 @@ var ConfigEntrySchema = object({
14330
14358
  value: unknown(),
14331
14359
  description: string().optional()
14332
14360
  });
14361
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
14362
+ /** One resolved linked device — the compact projection consumers need. */
14363
+ var LinkedDeviceSchema = object({
14364
+ deviceId: number(),
14365
+ name: string(),
14366
+ location: string().nullable(),
14367
+ features: array(string())
14368
+ });
14333
14369
  var SavedDeviceRowSchema = object({
14334
14370
  /** Numeric id reserved at allocateDeviceId time. */
14335
14371
  id: number(),
@@ -14551,7 +14587,10 @@ method(object({
14551
14587
  }), _void(), {
14552
14588
  kind: "mutation",
14553
14589
  auth: "admin"
14554
- }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
14590
+ }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
14591
+ mode: DeviceLinkModeSchema,
14592
+ devices: array(LinkedDeviceSchema)
14593
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
14555
14594
  deviceId: number(),
14556
14595
  values: record(string(), unknown())
14557
14596
  }), object({ success: literal(true) }), {
@@ -15820,6 +15859,61 @@ var EventKindSchema = _enum([
15820
15859
  "object",
15821
15860
  "audio"
15822
15861
  ]);
15862
+ /**
15863
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
15864
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
15865
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
15866
+ */
15867
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
15868
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
15869
+ var EventKindIconSchema = _enum([
15870
+ "motion",
15871
+ "audio",
15872
+ "person",
15873
+ "vehicle",
15874
+ "animal",
15875
+ "door",
15876
+ "pir",
15877
+ "smoke",
15878
+ "water",
15879
+ "button",
15880
+ "generic"
15881
+ ]);
15882
+ var EventKindCategorySchema = _enum([
15883
+ "motion",
15884
+ "audio",
15885
+ "detection",
15886
+ "sensor",
15887
+ "custom"
15888
+ ]);
15889
+ var EventKindDescriptorSchema = object({
15890
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
15891
+ kind: string(),
15892
+ label: string(),
15893
+ /** Hex color for timeline/legend rendering. */
15894
+ color: string(),
15895
+ icon: EventKindIconSchema,
15896
+ category: EventKindCategorySchema,
15897
+ /** Which cap + device contributes this kind. For built-ins the camera
15898
+ * itself; for sensor kinds the LINKED source device. */
15899
+ source: object({
15900
+ capName: string(),
15901
+ deviceId: number()
15902
+ })
15903
+ });
15904
+ var SensorEventSchema = object({
15905
+ id: string(),
15906
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
15907
+ * yields N rows, one per camera). */
15908
+ deviceId: number(),
15909
+ /** The linked sensor device whose state changed. */
15910
+ sourceDeviceId: number(),
15911
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
15912
+ kind: string(),
15913
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
15914
+ value: record(string(), unknown()).nullable(),
15915
+ timestamp: number()
15916
+ });
15823
15917
  var TrackPositionSchema = object({
15824
15918
  x: number(),
15825
15919
  y: number(),
@@ -15833,6 +15927,30 @@ var TrackSnapshotSchema = object({
15833
15927
  mediaKey: string()
15834
15928
  });
15835
15929
  /**
15930
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
15931
+ * divided by the track's detection-frame dims), computed at persist time.
15932
+ * Absent when the frame dims were unknown when the track was persisted
15933
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
15934
+ */
15935
+ var TrackEnvelopeSchema = object({
15936
+ minX: number(),
15937
+ minY: number(),
15938
+ maxX: number(),
15939
+ maxY: number()
15940
+ });
15941
+ /**
15942
+ * Row projection for track list queries. `full` (default) returns the
15943
+ * complete Track including the frame-rate `positions[]` history and the
15944
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
15945
+ * keeps every scalar the list surfaces actually render (ids, class(es),
15946
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
15947
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
15948
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
15949
+ * `getTrack`. Mirrors the event-store `projection` convention
15950
+ * (`getObjectEvents` et al.).
15951
+ */
15952
+ var TrackProjectionSchema = _enum(["full", "slim"]);
15953
+ /**
15836
15954
  * One audio-classification label heard on the track's camera while the
15837
15955
  * track was alive, aggregated per label. An "episode" is one persisted
15838
15956
  * audio event (the confident-classification path: score ≥ the device's
@@ -15883,7 +16001,11 @@ var TrackSchema = object({
15883
16001
  /** Audio-classification labels heard on the camera during the track's
15884
16002
  * life (score ≥ device `classificationMinScore`), aggregated per label.
15885
16003
  * Absent on legacy rows / tracks with no confident audio. */
15886
- audioLabels: array(TrackAudioLabelSchema).readonly().optional()
16004
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
16005
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
16006
+ * Populated from the persisted envelope columns on historical reads;
16007
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
16008
+ envelope: TrackEnvelopeSchema.optional()
15887
16009
  });
15888
16010
  var BaseEventFields = {
15889
16011
  id: string(),
@@ -15993,11 +16115,13 @@ var MediaFileSchema = object({
15993
16115
  sizeBytes: number(),
15994
16116
  timestamp: number()
15995
16117
  });
16118
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
16119
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
15996
16120
  var DeviceEventQueryInput = object({
15997
16121
  deviceId: number(),
15998
16122
  since: number().optional(),
15999
16123
  until: number().optional(),
16000
- limit: number().int().min(1).max(5e3).default(1e3),
16124
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
16001
16125
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
16002
16126
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
16003
16127
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -16005,6 +16129,27 @@ var DeviceEventQueryInput = object({
16005
16129
  projection: _enum(["full", "slim"]).optional()
16006
16130
  });
16007
16131
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
16132
+ var RecentTracksQueryInput = object({
16133
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
16134
+ deviceIds: array(number()),
16135
+ /** Window lower bound on `lastSeen` (inclusive). */
16136
+ since: number().optional(),
16137
+ /** Window upper bound on `lastSeen` (inclusive). */
16138
+ until: number().optional(),
16139
+ /** Page size. Default 200, max 1000. */
16140
+ limit: number().int().min(1).max(1e3).default(200),
16141
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
16142
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
16143
+ cursor: string().optional(),
16144
+ /** See {@link TrackProjectionSchema}. Default `full`. */
16145
+ projection: TrackProjectionSchema.optional()
16146
+ });
16147
+ var RecentTracksPageSchema = object({
16148
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
16149
+ tracks: array(TrackSchema).readonly(),
16150
+ /** Cursor for the next page, or null when this page is the last. */
16151
+ nextCursor: string().nullable()
16152
+ });
16008
16153
  var KeyEventQueryInput = object({
16009
16154
  deviceId: number(),
16010
16155
  /** Window lower bound (track firstSeen ≥ since). */
@@ -16087,11 +16232,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16087
16232
  deviceId: number(),
16088
16233
  since: number().optional(),
16089
16234
  until: number().optional(),
16090
- limit: number().optional()
16091
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
16092
- kind: "mutation",
16093
- auth: "admin"
16094
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
16235
+ limit: number().optional(),
16236
+ /** Spatial filter only tracks whose trajectory intersects the zone
16237
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
16238
+ * envelope columns, then precisely tested per position. Tracks with
16239
+ * an unknown envelope (no frame dims at persist time) always match. */
16240
+ zone: TrackZoneFilterSchema.optional(),
16241
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
16242
+ * compatible — omitting the field keeps today's exact behaviour). */
16243
+ projection: TrackProjectionSchema.optional()
16244
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
16245
+ kind: "mutation",
16246
+ auth: "admin"
16247
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
16248
+ deviceId: number(),
16249
+ since: number().optional(),
16250
+ until: number().optional(),
16251
+ kinds: array(string()).optional(),
16252
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
16253
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
16095
16254
  deviceId: number(),
16096
16255
  since: number(),
16097
16256
  until: number(),
@@ -19292,7 +19451,12 @@ var RecordingStorageUsageSchema = object({
19292
19451
  /**
19293
19452
  * Result of locating footage at a wall-clock instant for one device/profile.
19294
19453
  * `segment` carries the covering segment's window; `gap` reports the forward
19295
- * nearest covered edge (`null` past the end of footage / when none exists).
19454
+ * nearest covered edge (`null` past the end of footage / when none exists)
19455
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
19456
+ * footage behind the epoch; `null` when none — optional so older providers
19457
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
19458
+ * small inter-segment cracks (durMs under-covers the span to the next
19459
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
19296
19460
  */
19297
19461
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
19298
19462
  kind: literal("segment"),
@@ -19301,7 +19465,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
19301
19465
  bytes: number()
19302
19466
  }), object({
19303
19467
  kind: literal("gap"),
19304
- nearestEdgeMs: number().nullable()
19468
+ nearestEdgeMs: number().nullable(),
19469
+ prevEndMs: number().nullable().optional()
19305
19470
  })]);
19306
19471
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
19307
19472
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -20870,6 +21035,12 @@ Object.freeze({
20870
21035
  addonId: null,
20871
21036
  access: "view"
20872
21037
  },
21038
+ "deviceManager.getLinkedDevices": {
21039
+ capName: "device-manager",
21040
+ capScope: "system",
21041
+ addonId: null,
21042
+ access: "view"
21043
+ },
20873
21044
  "deviceManager.getRoleDisplayDefaults": {
20874
21045
  capName: "device-manager",
20875
21046
  capScope: "system",
@@ -22424,6 +22595,12 @@ Object.freeze({
22424
22595
  addonId: null,
22425
22596
  access: "view"
22426
22597
  },
22598
+ "pipelineAnalytics.getSensorEvents": {
22599
+ capName: "pipeline-analytics",
22600
+ capScope: "device",
22601
+ addonId: null,
22602
+ access: "view"
22603
+ },
22427
22604
  "pipelineAnalytics.getTrack": {
22428
22605
  capName: "pipeline-analytics",
22429
22606
  capScope: "device",
@@ -22436,6 +22613,18 @@ Object.freeze({
22436
22613
  addonId: null,
22437
22614
  access: "view"
22438
22615
  },
22616
+ "pipelineAnalytics.listEventKinds": {
22617
+ capName: "pipeline-analytics",
22618
+ capScope: "device",
22619
+ addonId: null,
22620
+ access: "view"
22621
+ },
22622
+ "pipelineAnalytics.listRecentTracks": {
22623
+ capName: "pipeline-analytics",
22624
+ capScope: "device",
22625
+ addonId: null,
22626
+ access: "view"
22627
+ },
22439
22628
  "pipelineAnalytics.listTracks": {
22440
22629
  capName: "pipeline-analytics",
22441
22630
  capScope: "device",
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { spawn } from "node:child_process";
3
- //#region ../types/dist/event-category-CFZs3jI4.mjs
3
+ //#region ../types/dist/event-category-H4AVePnn.mjs
4
4
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
5
5
  EventCategory["SystemBoot"] = "system.boot";
6
6
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -150,6 +150,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
150
150
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
151
151
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
152
152
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
153
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
154
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
155
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
153
156
  EventCategory["DetectionEvent"] = "detection.event";
154
157
  EventCategory["SessionTrackNew"] = "session.track.new";
155
158
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -7231,6 +7234,24 @@ var RecordingRetentionSchema = object({
7231
7234
  maxSizeGb: number().min(0).optional()
7232
7235
  });
7233
7236
  /**
7237
+ * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7238
+ * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7239
+ * previews at. Five graduated steps; absent on a config = `standard` (the
7240
+ * shipped default, matching `sheet-geometry`/`sheet-composer`).
7241
+ *
7242
+ * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7243
+ * Each window's index sidecar carries its own tile dims, so a camera whose
7244
+ * preset changed over time renders every historical window at the dims it was
7245
+ * written with.
7246
+ */
7247
+ var ScrubThumbnailPresetSchema = _enum([
7248
+ "minimal",
7249
+ "low",
7250
+ "standard",
7251
+ "high",
7252
+ "max"
7253
+ ]);
7254
+ /**
7234
7255
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7235
7256
  *
7236
7257
  * `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
@@ -7267,7 +7288,14 @@ var RecordingConfigSchema = object({
7267
7288
  * derived into bands once via `migrateConfigToBands`.
7268
7289
  */
7269
7290
  bands: array(RecordingBandSchema).optional(),
7270
- retention: RecordingRetentionSchema.optional()
7291
+ retention: RecordingRetentionSchema.optional(),
7292
+ /**
7293
+ * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7294
+ * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7295
+ * windows only — existing sheets are immutable, and each window's index
7296
+ * carries its own tile dims so mixed-preset history renders correctly.
7297
+ */
7298
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7271
7299
  });
7272
7300
  /**
7273
7301
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -9826,7 +9854,7 @@ object({
9826
9854
  deviceId: number(),
9827
9855
  timestamp: number()
9828
9856
  });
9829
- DeviceType.Button;
9857
+ DeviceType.Button, DeviceType.Camera;
9830
9858
  /**
9831
9859
  * Enum-state sensor — a string value picked from a finite option set.
9832
9860
  * Drives HA `sensor` entries with `state_class: enum` (HVAC action
@@ -14326,6 +14354,14 @@ var ConfigEntrySchema = object({
14326
14354
  value: unknown(),
14327
14355
  description: string().optional()
14328
14356
  });
14357
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
14358
+ /** One resolved linked device — the compact projection consumers need. */
14359
+ var LinkedDeviceSchema = object({
14360
+ deviceId: number(),
14361
+ name: string(),
14362
+ location: string().nullable(),
14363
+ features: array(string())
14364
+ });
14329
14365
  var SavedDeviceRowSchema = object({
14330
14366
  /** Numeric id reserved at allocateDeviceId time. */
14331
14367
  id: number(),
@@ -14547,7 +14583,10 @@ method(object({
14547
14583
  }), _void(), {
14548
14584
  kind: "mutation",
14549
14585
  auth: "admin"
14550
- }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
14586
+ }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
14587
+ mode: DeviceLinkModeSchema,
14588
+ devices: array(LinkedDeviceSchema)
14589
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
14551
14590
  deviceId: number(),
14552
14591
  values: record(string(), unknown())
14553
14592
  }), object({ success: literal(true) }), {
@@ -15816,6 +15855,61 @@ var EventKindSchema = _enum([
15816
15855
  "object",
15817
15856
  "audio"
15818
15857
  ]);
15858
+ /**
15859
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
15860
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
15861
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
15862
+ */
15863
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
15864
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
15865
+ var EventKindIconSchema = _enum([
15866
+ "motion",
15867
+ "audio",
15868
+ "person",
15869
+ "vehicle",
15870
+ "animal",
15871
+ "door",
15872
+ "pir",
15873
+ "smoke",
15874
+ "water",
15875
+ "button",
15876
+ "generic"
15877
+ ]);
15878
+ var EventKindCategorySchema = _enum([
15879
+ "motion",
15880
+ "audio",
15881
+ "detection",
15882
+ "sensor",
15883
+ "custom"
15884
+ ]);
15885
+ var EventKindDescriptorSchema = object({
15886
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
15887
+ kind: string(),
15888
+ label: string(),
15889
+ /** Hex color for timeline/legend rendering. */
15890
+ color: string(),
15891
+ icon: EventKindIconSchema,
15892
+ category: EventKindCategorySchema,
15893
+ /** Which cap + device contributes this kind. For built-ins the camera
15894
+ * itself; for sensor kinds the LINKED source device. */
15895
+ source: object({
15896
+ capName: string(),
15897
+ deviceId: number()
15898
+ })
15899
+ });
15900
+ var SensorEventSchema = object({
15901
+ id: string(),
15902
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
15903
+ * yields N rows, one per camera). */
15904
+ deviceId: number(),
15905
+ /** The linked sensor device whose state changed. */
15906
+ sourceDeviceId: number(),
15907
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
15908
+ kind: string(),
15909
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
15910
+ value: record(string(), unknown()).nullable(),
15911
+ timestamp: number()
15912
+ });
15819
15913
  var TrackPositionSchema = object({
15820
15914
  x: number(),
15821
15915
  y: number(),
@@ -15829,6 +15923,30 @@ var TrackSnapshotSchema = object({
15829
15923
  mediaKey: string()
15830
15924
  });
15831
15925
  /**
15926
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
15927
+ * divided by the track's detection-frame dims), computed at persist time.
15928
+ * Absent when the frame dims were unknown when the track was persisted
15929
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
15930
+ */
15931
+ var TrackEnvelopeSchema = object({
15932
+ minX: number(),
15933
+ minY: number(),
15934
+ maxX: number(),
15935
+ maxY: number()
15936
+ });
15937
+ /**
15938
+ * Row projection for track list queries. `full` (default) returns the
15939
+ * complete Track including the frame-rate `positions[]` history and the
15940
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
15941
+ * keeps every scalar the list surfaces actually render (ids, class(es),
15942
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
15943
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
15944
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
15945
+ * `getTrack`. Mirrors the event-store `projection` convention
15946
+ * (`getObjectEvents` et al.).
15947
+ */
15948
+ var TrackProjectionSchema = _enum(["full", "slim"]);
15949
+ /**
15832
15950
  * One audio-classification label heard on the track's camera while the
15833
15951
  * track was alive, aggregated per label. An "episode" is one persisted
15834
15952
  * audio event (the confident-classification path: score ≥ the device's
@@ -15879,7 +15997,11 @@ var TrackSchema = object({
15879
15997
  /** Audio-classification labels heard on the camera during the track's
15880
15998
  * life (score ≥ device `classificationMinScore`), aggregated per label.
15881
15999
  * Absent on legacy rows / tracks with no confident audio. */
15882
- audioLabels: array(TrackAudioLabelSchema).readonly().optional()
16000
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
16001
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
16002
+ * Populated from the persisted envelope columns on historical reads;
16003
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
16004
+ envelope: TrackEnvelopeSchema.optional()
15883
16005
  });
15884
16006
  var BaseEventFields = {
15885
16007
  id: string(),
@@ -15989,11 +16111,13 @@ var MediaFileSchema = object({
15989
16111
  sizeBytes: number(),
15990
16112
  timestamp: number()
15991
16113
  });
16114
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
16115
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
15992
16116
  var DeviceEventQueryInput = object({
15993
16117
  deviceId: number(),
15994
16118
  since: number().optional(),
15995
16119
  until: number().optional(),
15996
- limit: number().int().min(1).max(5e3).default(1e3),
16120
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
15997
16121
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
15998
16122
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
15999
16123
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -16001,6 +16125,27 @@ var DeviceEventQueryInput = object({
16001
16125
  projection: _enum(["full", "slim"]).optional()
16002
16126
  });
16003
16127
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
16128
+ var RecentTracksQueryInput = object({
16129
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
16130
+ deviceIds: array(number()),
16131
+ /** Window lower bound on `lastSeen` (inclusive). */
16132
+ since: number().optional(),
16133
+ /** Window upper bound on `lastSeen` (inclusive). */
16134
+ until: number().optional(),
16135
+ /** Page size. Default 200, max 1000. */
16136
+ limit: number().int().min(1).max(1e3).default(200),
16137
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
16138
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
16139
+ cursor: string().optional(),
16140
+ /** See {@link TrackProjectionSchema}. Default `full`. */
16141
+ projection: TrackProjectionSchema.optional()
16142
+ });
16143
+ var RecentTracksPageSchema = object({
16144
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
16145
+ tracks: array(TrackSchema).readonly(),
16146
+ /** Cursor for the next page, or null when this page is the last. */
16147
+ nextCursor: string().nullable()
16148
+ });
16004
16149
  var KeyEventQueryInput = object({
16005
16150
  deviceId: number(),
16006
16151
  /** Window lower bound (track firstSeen ≥ since). */
@@ -16083,11 +16228,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16083
16228
  deviceId: number(),
16084
16229
  since: number().optional(),
16085
16230
  until: number().optional(),
16086
- limit: number().optional()
16087
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
16088
- kind: "mutation",
16089
- auth: "admin"
16090
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
16231
+ limit: number().optional(),
16232
+ /** Spatial filter only tracks whose trajectory intersects the zone
16233
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
16234
+ * envelope columns, then precisely tested per position. Tracks with
16235
+ * an unknown envelope (no frame dims at persist time) always match. */
16236
+ zone: TrackZoneFilterSchema.optional(),
16237
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
16238
+ * compatible — omitting the field keeps today's exact behaviour). */
16239
+ projection: TrackProjectionSchema.optional()
16240
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
16241
+ kind: "mutation",
16242
+ auth: "admin"
16243
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
16244
+ deviceId: number(),
16245
+ since: number().optional(),
16246
+ until: number().optional(),
16247
+ kinds: array(string()).optional(),
16248
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
16249
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
16091
16250
  deviceId: number(),
16092
16251
  since: number(),
16093
16252
  until: number(),
@@ -19288,7 +19447,12 @@ var RecordingStorageUsageSchema = object({
19288
19447
  /**
19289
19448
  * Result of locating footage at a wall-clock instant for one device/profile.
19290
19449
  * `segment` carries the covering segment's window; `gap` reports the forward
19291
- * nearest covered edge (`null` past the end of footage / when none exists).
19450
+ * nearest covered edge (`null` past the end of footage / when none exists)
19451
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
19452
+ * footage behind the epoch; `null` when none — optional so older providers
19453
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
19454
+ * small inter-segment cracks (durMs under-covers the span to the next
19455
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
19292
19456
  */
19293
19457
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
19294
19458
  kind: literal("segment"),
@@ -19297,7 +19461,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
19297
19461
  bytes: number()
19298
19462
  }), object({
19299
19463
  kind: literal("gap"),
19300
- nearestEdgeMs: number().nullable()
19464
+ nearestEdgeMs: number().nullable(),
19465
+ prevEndMs: number().nullable().optional()
19301
19466
  })]);
19302
19467
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
19303
19468
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -20866,6 +21031,12 @@ Object.freeze({
20866
21031
  addonId: null,
20867
21032
  access: "view"
20868
21033
  },
21034
+ "deviceManager.getLinkedDevices": {
21035
+ capName: "device-manager",
21036
+ capScope: "system",
21037
+ addonId: null,
21038
+ access: "view"
21039
+ },
20869
21040
  "deviceManager.getRoleDisplayDefaults": {
20870
21041
  capName: "device-manager",
20871
21042
  capScope: "system",
@@ -22420,6 +22591,12 @@ Object.freeze({
22420
22591
  addonId: null,
22421
22592
  access: "view"
22422
22593
  },
22594
+ "pipelineAnalytics.getSensorEvents": {
22595
+ capName: "pipeline-analytics",
22596
+ capScope: "device",
22597
+ addonId: null,
22598
+ access: "view"
22599
+ },
22423
22600
  "pipelineAnalytics.getTrack": {
22424
22601
  capName: "pipeline-analytics",
22425
22602
  capScope: "device",
@@ -22432,6 +22609,18 @@ Object.freeze({
22432
22609
  addonId: null,
22433
22610
  access: "view"
22434
22611
  },
22612
+ "pipelineAnalytics.listEventKinds": {
22613
+ capName: "pipeline-analytics",
22614
+ capScope: "device",
22615
+ addonId: null,
22616
+ access: "view"
22617
+ },
22618
+ "pipelineAnalytics.listRecentTracks": {
22619
+ capName: "pipeline-analytics",
22620
+ capScope: "device",
22621
+ addonId: null,
22622
+ access: "view"
22623
+ },
22435
22624
  "pipelineAnalytics.listTracks": {
22436
22625
  capName: "pipeline-analytics",
22437
22626
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-decoder-ffmpeg",
3
- "version": "1.1.14",
3
+ "version": "1.1.16",
4
4
  "description": "Standalone ffmpeg-subprocess decoder fallback addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",