@camstack/types 1.2.272 → 1.2.274

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/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_sleep = require("./sleep-DYGSsCgK.js");
2
+ const require_sleep = require("./sleep-WOeeZ4FU.js");
3
3
  const require_event_category = require("./event-category-MbnB-6AN.js");
4
4
  const require_composition = require("./composition-BRxAnxtU.js");
5
5
  const require_err_msg = require("./err-msg-Dx2Kor0g.js");
@@ -16,7 +16,7 @@ let zod = require("zod");
16
16
  * ```
17
17
  *
18
18
  * The owner type is a TABLE. That is already true of every owner on the live
19
- * hub - `track`, `summary`, `face`, `identity`, `plate` - and the exception was
19
+ * hub - `track`, `face`, `identity`, `plate` - and the exception was
20
20
  * `event`, which covered three tables at once. While event ids were UUIDs the
21
21
  * ambiguity cost nothing, because a UUID is unique across tables. D474 made an
22
22
  * event id a SQLite rowid alias, so each table numbers from 1 and `event:5`
@@ -62,10 +62,13 @@ let zod = require("zod");
62
62
  * are declared owner kinds with no rows today, and the parser would have
63
63
  * rejected their keys the day one appeared. A list derived from data is a list
64
64
  * that is correct until the data changes.
65
+ *
66
+ * `summary` is gone with the session summary it named (D680): a `summary:` key
67
+ * left on disk is unreadable by design, like a legacy `event:` one, and the
68
+ * orphan audit reclaims its row as a retired owner.
65
69
  */
66
70
  var MEDIA_OWNER_TYPES = [
67
71
  "track",
68
- "summary",
69
72
  "face",
70
73
  "identity",
71
74
  "plate",
@@ -655,6 +658,92 @@ async function resolveEgressDecodeHwAccel(deps) {
655
658
  return resolved;
656
659
  }
657
660
  //#endregion
661
+ //#region src/ffmpeg/gif-derive.ts
662
+ /**
663
+ * The notification GIF's filter chain and argv — ONE definition for every
664
+ * process that derives a gif from a notification video.
665
+ *
666
+ * ## Why this lives in `@camstack/types`
667
+ *
668
+ * Two addons derive a notification gif from an mp4, and they may never import
669
+ * each other:
670
+ *
671
+ * - the stream broker (`addon-pipeline`), from the clip it just cut out of its
672
+ * own ring (`gif-from-mp4.ts`);
673
+ * - the notification centre (`addon-post-analysis`), from a clip cut out of
674
+ * the RECORDINGS when the moment is older than any ring holds
675
+ * ([D682](../../../../docs/decisions/adr-0682-a-pick-ups-footage-is-cut-around-the-pick-up.md)).
676
+ *
677
+ * Two copies of this chain would be two gifs of the same kind of moment that
678
+ * look different depending on how old the moment was — the class of defect
679
+ * D85/D91 already paid for once. So the chain is here, pure and dependency
680
+ * free, and each caller owns only its own process lifecycle.
681
+ *
682
+ * ## The palette was never the problem
683
+ *
684
+ * The `split` + `palettegen(stats_mode=diff)` + `paletteuse(bayer)` pair is
685
+ * right and stays. The measured cause of "the gif looks de-pixellated" was a
686
+ * 640×360 substream scaled DOWN to 480 px, upstream of the palette. The `split`
687
+ * form is a single READ of the input, not a lesser two-pass.
688
+ */
689
+ /**
690
+ * Format a frame rate for an ffmpeg filter argument.
691
+ *
692
+ * `12 / 4` must reach the filter as `3` and `15 / 4` as `3.75`, not as a float
693
+ * tail that `av_parse_video_rate` then has to guess at.
694
+ */
695
+ function ffmpegRate(rate) {
696
+ return String(Math.round(rate * 1e3) / 1e3);
697
+ }
698
+ /**
699
+ * The factor the derive must apply ON TOP of the speed the mp4 already carries.
700
+ *
701
+ * The gif is derived from a muxed mp4, so if that mux already ran `setpts` at
702
+ * the caller's `speed`, applying the absolute gif rate again would compound
703
+ * them — a 4× gif beside a 2× video would come out at 8×. Discounting here is
704
+ * what keeps the gif rate ABSOLUTE against real time whatever the video does.
705
+ */
706
+ function gifDeriveSpeed(gifSpeed, videoSpeed) {
707
+ if (!Number.isFinite(videoSpeed) || videoSpeed <= 0) return gifSpeed;
708
+ return gifSpeed / videoSpeed;
709
+ }
710
+ /**
711
+ * The filter chain. Extracted so a test can assert the palette pair without
712
+ * spawning ffmpeg — an argv test cannot see quality, but it can see a filter
713
+ * that somebody deleted.
714
+ *
715
+ * `fps` BEFORE `setpts`: decimating an already sped-up timeline yields `speed`
716
+ * times the frames asked for, and a gif pays for every one. So the decimation
717
+ * runs at `fps / speed` in SOURCE time and `setpts` multiplies what survived.
718
+ * The trailing `fps` re-declares the rate, which is NOT redundant: `setpts`
719
+ * leaves the filter link advertising the sampled rate and the gif muxer
720
+ * believes the link — measured on 615 as a 3.01 s gif of ten frames.
721
+ */
722
+ function gifFilterComplex(input) {
723
+ const speed = input.speed > 0 ? input.speed : 1;
724
+ if (speed === 1) return `[0:v]fps=${ffmpegRate(input.fps)},scale=${input.maxWidth}:-2:flags=lanczos,split[a][b];[a]palettegen=stats_mode=diff[p];[b][p]paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle`;
725
+ return `[0:v]fps=${ffmpegRate(input.fps / speed)},setpts=PTS/${speed},fps=${ffmpegRate(input.fps)},scale=${input.maxWidth}:-2:flags=lanczos,split[a][b];[a]palettegen=stats_mode=diff[p];[b][p]paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle`;
726
+ }
727
+ /** The full argv: mp4 in, gif on stdout. */
728
+ function gifFromMp4Args(input) {
729
+ return [
730
+ "-y",
731
+ "-v",
732
+ "error",
733
+ "-f",
734
+ "mp4",
735
+ "-i",
736
+ input.input ?? "pipe:0",
737
+ "-filter_complex",
738
+ gifFilterComplex(input),
739
+ "-loop",
740
+ "0",
741
+ "-f",
742
+ "gif",
743
+ "pipe:1"
744
+ ];
745
+ }
746
+ //#endregion
658
747
  //#region src/ffmpeg/sharing-key.ts
659
748
  /**
660
749
  * The sharing key for `streamBroker.acquireEgressTranscode`.
@@ -21735,6 +21824,21 @@ var ObjectEventSchema = zod.z.object({
21735
21824
  keyFrameMediaId: zod.z.number().int().optional(),
21736
21825
  /** Populated by B5 (recording playback URL for this event). */
21737
21826
  mediaUrl: zod.z.string().optional(),
21827
+ /**
21828
+ * When the change this row records HAPPENED, epoch ms, when that is earlier
21829
+ * than {@link timestamp} and the producer could date it (D684).
21830
+ *
21831
+ * Set on a package PICK-UP row only: its `timestamp` is when the departure
21832
+ * was CONCLUDED — five minutes of unseen observed time after the parcel went
21833
+ * — and a notification's footage has to be cut around the pick-up, not the
21834
+ * empty doormat after it (D682). Stored so every path that rebuilds a
21835
+ * notification subject from the row (the boot reconcile, a dry-run) anchors
21836
+ * on the same instant the live hook did.
21837
+ *
21838
+ * `timestamp` keeps its meaning. Absent on every other row and on every
21839
+ * pick-up written before D684 — UNKNOWN, never 0 and never `timestamp`.
21840
+ */
21841
+ observedAt: zod.z.number().optional(),
21738
21842
  /** The parent track's key-event importance [0,1], propagated to every object
21739
21843
  * event of the track (so an event row can be sorted by importance without a
21740
21844
  * track join). Absent on legacy rows / before the track was scored. */
@@ -22080,143 +22184,6 @@ var RecentTracksQueryInput = zod.z.object({
22080
22184
  /** See {@link ExcludeSourcesDoc}. */
22081
22185
  excludeSources: zod.z.array(TrackSourceSchema).optional()
22082
22186
  });
22083
- /**
22084
- * A Summary (D359): the per-camera session envelope that references tracks.
22085
- * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
22086
- * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
22087
- * first frame with a member visible has been materialised (D482 — it was the
22088
- * row's composite key, which embedded the owner and could name another table's
22089
- * row once event ids became per-table rowids).
22090
- */
22091
- var SummarySchema = zod.z.object({
22092
- id: zod.z.string(),
22093
- deviceId: zod.z.number(),
22094
- openedAt: zod.z.number(),
22095
- lastActiveAt: zod.z.number(),
22096
- sealedAt: zod.z.number().nullable(),
22097
- memberCount: zod.z.number().int().nonnegative(),
22098
- groupShotMediaId: zod.z.number().int().nullable()
22099
- });
22100
- var SUMMARIES_MAX_LIMIT = 200;
22101
- var SUMMARIES_DEFAULT_LIMIT = 50;
22102
- var SummariesQueryInput = zod.z.object({
22103
- deviceIds: zod.z.array(zod.z.number()).min(1),
22104
- since: zod.z.number().optional(),
22105
- until: zod.z.number().optional(),
22106
- limit: zod.z.number().int().min(1).max(200).default(50),
22107
- /** The same member-wise filter {@link listRecentSummaries} takes — one
22108
- * meaning of "dog" on every summary surface. */
22109
- classes: zod.z.array(zod.z.string().min(1).max(60)).max(40).optional()
22110
- });
22111
- /**
22112
- * One member of a group, as a reader needs it.
22113
- *
22114
- * The class and the role travel WITH the id because every consumer that shows
22115
- * a group has to say what it was made of — "two people and a dog" — and asking
22116
- * `getTrack` per member to find that out turns one card into `memberCount`
22117
- * round trips. The member row already holds both (they are written at the
22118
- * join), so this costs the reader nothing.
22119
- */
22120
- var SummaryMemberSchema = zod.z.object({
22121
- trackId: zod.z.string(),
22122
- /**
22123
- * The track's class at the time it JOINED, or `null` when it was unresolved
22124
- * and the track no longer exists to fill it.
22125
- *
22126
- * The join, not the latest opinion: class voting can re-decide a track's
22127
- * class afterwards, and a group's composition is the history of who came
22128
- * together. A consumer counts these to badge a thumbnail — "two people and a
22129
- * dog" — rather than being handed a second, denormalised tally that would
22130
- * drift from the members it claims to describe.
22131
- */
22132
- className: zod.z.string().nullable(),
22133
- /** `primary` is the one member that carries the group's identity. */
22134
- role: zod.z.enum([
22135
- "primary",
22136
- "sibling",
22137
- "child"
22138
- ]).nullable(),
22139
- joinedAt: zod.z.number(),
22140
- /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
22141
- * at read time. Absent when nothing resolved, never an empty string. */
22142
- label: zod.z.string().optional(),
22143
- /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
22144
- * recognised nobody, and absent for a member whose track has been pruned —
22145
- * a thumbnail that names the wrong person is worse than one naming nobody. */
22146
- subLabel: zod.z.string().optional()
22147
- });
22148
- var SummaryDetailSchema = zod.z.object({
22149
- summary: SummarySchema,
22150
- /** Members in absorption order, oldest join first. */
22151
- members: zod.z.array(SummaryMemberSchema).readonly()
22152
- });
22153
- /**
22154
- * What a LIST of groups carries: the envelope plus what it is MADE OF.
22155
- *
22156
- * Coarse on purpose — the operator's rule is "only the rough data on the list,
22157
- * we extend when we notice we need more". Enough to draw the card ("two people
22158
- * and a dog") and no more. The MEMBERS, their names and the per-member links
22159
- * are on {@link getSummary}: shipping every member of every group so a client
22160
- * can count them turns a page of two hundred groups into a thousand records to
22161
- * draw two hundred badges.
22162
- */
22163
- var SummaryListItemSchema = zod.z.object({
22164
- summary: SummarySchema,
22165
- /** Class counts, most numerous first; `className: null` is a member whose
22166
- * class never resolved, counted so the counts add up to `memberCount`. */
22167
- classes: zod.z.array(zod.z.object({
22168
- className: zod.z.string().nullable(),
22169
- count: zod.z.number().int().positive()
22170
- })).readonly()
22171
- });
22172
- var SummariesPageSchema = zod.z.object({ summaries: zod.z.array(SummaryListItemSchema).readonly() });
22173
- /**
22174
- * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
22175
- *
22176
- * The reel on the cameras page and the Events gallery both ride a cursor, so a
22177
- * summaries feed that could only answer "the last N for these cameras"
22178
- * (`listSummaries`) would make groups available in the timeline and nowhere
22179
- * else. Two feeds with different ordering contracts would be two ideas of what
22180
- * "newest" means, on one screen.
22181
- */
22182
- var RecentSummariesQueryInput = zod.z.object({
22183
- /** Devices to merge. An empty array yields an empty page. */
22184
- deviceIds: zod.z.array(zod.z.number()),
22185
- /** Window lower bound on `lastActiveAt` (inclusive). */
22186
- since: zod.z.number().optional(),
22187
- /** Window upper bound on `lastActiveAt` (inclusive). */
22188
- until: zod.z.number().optional(),
22189
- limit: zod.z.number().int().min(1).max(500).default(100),
22190
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
22191
- cursor: zod.z.string().optional(),
22192
- /**
22193
- * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
22194
- * deliberately the same name: the events filter tree emits one list of ticked
22195
- * taxonomy nodes, and a summaries feed that called it something else would
22196
- * make the operator's one filter component produce two shapes.
22197
- *
22198
- * A question about the MEMBERS: a group matches when ANY of them does. "Show
22199
- * me the arrivals with a dog" has to return the group of four people who had
22200
- * one with them; a filter on the group's own fields could only ever answer
22201
- * about its primary.
22202
- *
22203
- * Matched against all THREE tiers of a member — the class (`animal`), the
22204
- * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
22205
- * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
22206
- * an event kind in the tree and a tier-1 label on an `animal` track; a filter
22207
- * that read only the class would answer "animal" and never "dog".
22208
- *
22209
- * ABSENT or EMPTY means no filter — absent is not a selection of none.
22210
- */
22211
- classes: zod.z.array(zod.z.string().min(1).max(60)).max(40).optional()
22212
- });
22213
- var RecentSummariesPageSchema = zod.z.object({
22214
- /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
22215
- * {@link listSummaries} — one list shape, so a card renders the same
22216
- * whichever feed drew it. */
22217
- summaries: zod.z.array(SummaryListItemSchema).readonly(),
22218
- nextCursor: zod.z.string().nullable()
22219
- });
22220
22187
  var RecentTracksPageSchema = zod.z.object({
22221
22188
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
22222
22189
  tracks: zod.z.array(TrackSchema).readonly(),
@@ -22803,15 +22770,6 @@ var pipelineAnalyticsCapability = {
22803
22770
  * are not included (same contract as `listTracks`).
22804
22771
  */
22805
22772
  listRecentTracks: require_sleep.method(RecentTracksQueryInput, RecentTracksPageSchema),
22806
- /** Newest first by `openedAt`, for a camera set, inside an optional window. */
22807
- listSummaries: require_sleep.method(SummariesQueryInput, SummariesPageSchema),
22808
- /**
22809
- * The PAGED feed — the reel and the Events gallery. Same cursor contract as
22810
- * {@link listRecentTracks}, so one screen cannot hold two ideas of newest.
22811
- */
22812
- listRecentSummaries: require_sleep.method(RecentSummariesQueryInput, RecentSummariesPageSchema),
22813
- /** One summary with its member track ids; `null` for an id nobody wrote. */
22814
- getSummary: require_sleep.method(zod.z.object({ id: zod.z.string() }), SummaryDetailSchema.nullable()),
22815
22773
  clearTracks: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.void(), {
22816
22774
  kind: "mutation",
22817
22775
  auth: "admin"
@@ -55962,12 +55920,6 @@ var METHOD_ACCESS_MAP = Object.freeze({
55962
55920
  addonId: null,
55963
55921
  access: "view"
55964
55922
  },
55965
- "pipelineAnalytics.getSummary": {
55966
- capName: "pipeline-analytics",
55967
- capScope: "device",
55968
- addonId: null,
55969
- access: "view"
55970
- },
55971
55923
  "pipelineAnalytics.getTrack": {
55972
55924
  capName: "pipeline-analytics",
55973
55925
  capScope: "device",
@@ -56028,12 +55980,6 @@ var METHOD_ACCESS_MAP = Object.freeze({
56028
55980
  addonId: null,
56029
55981
  access: "view"
56030
55982
  },
56031
- "pipelineAnalytics.listRecentSummaries": {
56032
- capName: "pipeline-analytics",
56033
- capScope: "device",
56034
- addonId: null,
56035
- access: "view"
56036
- },
56037
55983
  "pipelineAnalytics.listRecentTracks": {
56038
55984
  capName: "pipeline-analytics",
56039
55985
  capScope: "device",
@@ -56064,12 +56010,6 @@ var METHOD_ACCESS_MAP = Object.freeze({
56064
56010
  addonId: null,
56065
56011
  access: "view"
56066
56012
  },
56067
- "pipelineAnalytics.listSummaries": {
56068
- capName: "pipeline-analytics",
56069
- capScope: "device",
56070
- addonId: null,
56071
- access: "view"
56072
- },
56073
56013
  "pipelineAnalytics.listTrackMedia": {
56074
56014
  capName: "pipeline-analytics",
56075
56015
  capScope: "device",
@@ -60574,11 +60514,6 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
60574
60514
  form: "single",
60575
60515
  optional: true
60576
60516
  }],
60577
- "pipelineAnalytics.listRecentSummaries": [{
60578
- name: "deviceIds",
60579
- form: "array",
60580
- optional: false
60581
- }],
60582
60517
  "pipelineAnalytics.listRecentTracks": [{
60583
60518
  name: "deviceIds",
60584
60519
  form: "array",
@@ -60589,11 +60524,6 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
60589
60524
  form: "array",
60590
60525
  optional: true
60591
60526
  }],
60592
- "pipelineAnalytics.listSummaries": [{
60593
- name: "deviceIds",
60594
- form: "array",
60595
- optional: false
60596
- }],
60597
60527
  "pipelineAnalytics.listTrackMedia": [{
60598
60528
  name: "deviceId",
60599
60529
  form: "single",
@@ -69525,8 +69455,6 @@ exports.STORAGE_LOCATION_MODES = STORAGE_LOCATION_MODES;
69525
69455
  exports.STREAM_PROFILE_META = STREAM_PROFILE_META;
69526
69456
  exports.STREAM_QUALITY_LABELS = STREAM_QUALITY_LABELS;
69527
69457
  exports.SUB_DETECTION_TYPES = SUB_DETECTION_TYPES;
69528
- exports.SUMMARIES_DEFAULT_LIMIT = SUMMARIES_DEFAULT_LIMIT;
69529
- exports.SUMMARIES_MAX_LIMIT = SUMMARIES_MAX_LIMIT;
69530
69458
  exports.SYSTEM_CAP_NAMES = SYSTEM_CAP_NAMES;
69531
69459
  exports.SYSTEM_SCOPE_DEVICE_METHODS = SYSTEM_SCOPE_DEVICE_METHODS;
69532
69460
  exports.SceneCheckSchema = SceneCheckSchema;
@@ -69636,7 +69564,6 @@ exports.SubscribeAudioChunksInputSchema = require_sleep.SubscribeAudioChunksInpu
69636
69564
  exports.SubscribeAudioChunksResultSchema = require_sleep.SubscribeAudioChunksResultSchema;
69637
69565
  exports.SubscribeFramesInputSchema = require_sleep.SubscribeFramesInputSchema;
69638
69566
  exports.SubscribeFramesResultSchema = require_sleep.SubscribeFramesResultSchema;
69639
- exports.SummarySchema = SummarySchema;
69640
69567
  exports.SwitchStatusSchema = SwitchStatusSchema;
69641
69568
  exports.SystemMetricsSchema = SystemMetricsSchema;
69642
69569
  exports.SystemMirror = SystemMirror;
@@ -69982,6 +69909,7 @@ exports.featureProbeCapability = featureProbeCapability;
69982
69909
  exports.featureSources = require_composition.featureSources;
69983
69910
  exports.feederCustomizationExample = feederCustomizationExample;
69984
69911
  exports.feederExampleSources = feederExampleSources;
69912
+ exports.ffmpegRate = ffmpegRate;
69985
69913
  exports.fieldReads = require_composition.fieldReads;
69986
69914
  exports.filesystemBrowseCapability = filesystemBrowseCapability;
69987
69915
  exports.findCandidateCycles = findCandidateCycles;
@@ -70001,6 +69929,9 @@ exports.getByPath = getByPath;
70001
69929
  exports.getCapsByProviderKind = getCapsByProviderKind;
70002
69930
  exports.getLogChannelRegistry = getLogChannelRegistry;
70003
69931
  exports.getTaxonomyEntry = getTaxonomyEntry;
69932
+ exports.gifDeriveSpeed = gifDeriveSpeed;
69933
+ exports.gifFilterComplex = gifFilterComplex;
69934
+ exports.gifFromMp4Args = gifFromMp4Args;
70004
69935
  exports.hasMotionTrigger = hasMotionTrigger;
70005
69936
  exports.hfModelUrl = hfModelUrl;
70006
69937
  exports.htmlToText = htmlToText;