@camstack/addon-post-analysis 1.2.117 → 1.2.118
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/{dist-Dk-BdVHw.js → dist-CO6pe7bi.js} +6 -1
- package/dist/{dist-PMOtjzh2.mjs → dist-Gn2fFMov.mjs} +6 -1
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BmR_bLi_.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BYzwylnw.mjs} +2 -2
- package/dist/pipeline-analytics/{hostInit-Ddl2vUu6.mjs → hostInit-TlKH3NQ8.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +271 -87
- package/dist/pipeline-analytics/index.mjs +271 -87
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as customAction, A as NcTaxonomySchema, At as sleep, B as TIMELAPSE_DENSE_FLOOR_SEC, Bt as string, C as NcRuleSchema, Ct as errMsg, D as NcSnoozeSchema, Dt as hydrateSchema, E as NcSnoozeInputSchema, Et as createEvent, Ft as literal, G as VISIT_MERGE_GAP_MS, H as TimelapseRulePatchSchema, Ht as EventCategory, I as SCENE_DEFAULT_ANCHOR_THRESHOLD, It as number, J as assertTimelapseCadences, K as addonWidgetsSourceCapability, L as SCENE_DEFAULT_UNCOVERED_POLICY, Lt as object, Mt as array, N as RECORDING_EXPORT_MAX_READ_BYTES, Nt as boolean, O as NcSnoozeSuppressedSchema, Ot as isDeviceScopedCap, P as RetrainStatusSchema, Pt as discriminatedUnion, Q as cosineSimilarity$1, R as SCENE_DIVERGED, Rt as partialRecord, S as NcRulePatchSchema, St as zoneAnalyticsCapability, T as NcScheduleSchema, Tt as DeviceType, U as TimelapseRuleSchema, V as TimelapseRuleInputSchema, Vt as unknown, W as TrackSourceSchema, X as audioModeOf, Y as audioMetricsCapability, Z as buildEventKindDescriptor, _ as NC_DEFAULT_SNOOZE_MINUTES, _t as sceneMonitorCapability, a as DEFAULT_EVENT_COLOR, b as NcConditionDescriptorSchema, bt as vectorDimFromBase64, ct as kebabToCamel, d as EVENT_PAD_MS, dt as pickClusterStepModels, et as defineCustomActions, f as FULL_IMAGE_BBOX, ft as pipelineAnalyticsCapability, g as NC_CONDITION_CATALOG, h as NC_ALARM_SYSTEM_EVENT_KINDS, ht as readTimelapseGeneratedAt, i as CLUSTER_MODEL_SCOPED_STEPS, it as faceGalleryCapability, j as OpsLogEntrySchema, jt as _enum, k as NcSystemEventKindSchema, kt as nodePin, l as DeclaredDevices, lt as notificationRulesCapability, m as MACRO_LABELS, mt as readDeviceStateFrom, n as AnalyticsGroupRecordSchema, ot as isDetectionMacroClass, p as LabelAttributionSchema, pt as plateGalleryCapability, q as alarmPanelCapability, r as BaseDevice, rt as encodeVectorBase64, s as DETECTION_MACRO_CLASSES, st as isScheduleActive, t as AnalyticsGroupDetailSchema, tt as deriveRecordingMode, u as EVENT_KIND_BY_CAP, vt as subKindsOf, w as NcRuleTargetSchema, wt as BaseAddon, x as NcRuleInputSchema, xt as videoclipsCapability, y as NC_TAXONOMY, yt as systemEventFilterApplies, z as SceneMonitorSchema, zt as record } from "../dist-
|
|
1
|
+
import { $ as customAction, A as NcTaxonomySchema, At as sleep, B as TIMELAPSE_DENSE_FLOOR_SEC, Bt as string, C as NcRuleSchema, Ct as errMsg, D as NcSnoozeSchema, Dt as hydrateSchema, E as NcSnoozeInputSchema, Et as createEvent, Ft as literal, G as VISIT_MERGE_GAP_MS, H as TimelapseRulePatchSchema, Ht as EventCategory, I as SCENE_DEFAULT_ANCHOR_THRESHOLD, It as number, J as assertTimelapseCadences, K as addonWidgetsSourceCapability, L as SCENE_DEFAULT_UNCOVERED_POLICY, Lt as object, Mt as array, N as RECORDING_EXPORT_MAX_READ_BYTES, Nt as boolean, O as NcSnoozeSuppressedSchema, Ot as isDeviceScopedCap, P as RetrainStatusSchema, Pt as discriminatedUnion, Q as cosineSimilarity$1, R as SCENE_DIVERGED, Rt as partialRecord, S as NcRulePatchSchema, St as zoneAnalyticsCapability, T as NcScheduleSchema, Tt as DeviceType, U as TimelapseRuleSchema, V as TimelapseRuleInputSchema, Vt as unknown, W as TrackSourceSchema, X as audioModeOf, Y as audioMetricsCapability, Z as buildEventKindDescriptor, _ as NC_DEFAULT_SNOOZE_MINUTES, _t as sceneMonitorCapability, a as DEFAULT_EVENT_COLOR, b as NcConditionDescriptorSchema, bt as vectorDimFromBase64, ct as kebabToCamel, d as EVENT_PAD_MS, dt as pickClusterStepModels, et as defineCustomActions, f as FULL_IMAGE_BBOX, ft as pipelineAnalyticsCapability, g as NC_CONDITION_CATALOG, h as NC_ALARM_SYSTEM_EVENT_KINDS, ht as readTimelapseGeneratedAt, i as CLUSTER_MODEL_SCOPED_STEPS, it as faceGalleryCapability, j as OpsLogEntrySchema, jt as _enum, k as NcSystemEventKindSchema, kt as nodePin, l as DeclaredDevices, lt as notificationRulesCapability, m as MACRO_LABELS, mt as readDeviceStateFrom, n as AnalyticsGroupRecordSchema, ot as isDetectionMacroClass, p as LabelAttributionSchema, pt as plateGalleryCapability, q as alarmPanelCapability, r as BaseDevice, rt as encodeVectorBase64, s as DETECTION_MACRO_CLASSES, st as isScheduleActive, t as AnalyticsGroupDetailSchema, tt as deriveRecordingMode, u as EVENT_KIND_BY_CAP, vt as subKindsOf, w as NcRuleTargetSchema, wt as BaseAddon, x as NcRuleInputSchema, xt as videoclipsCapability, y as NC_TAXONOMY, yt as systemEventFilterApplies, z as SceneMonitorSchema, zt as record } from "../dist-Gn2fFMov.mjs";
|
|
2
2
|
import { t as __exportAll } from "../embedding-encoder/index.mjs";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import { promises } from "node:fs";
|
|
@@ -25902,7 +25902,7 @@ var CLASS_RANK_VEHICLE = .8;
|
|
|
25902
25902
|
var CLASS_RANK_ANIMAL = .5;
|
|
25903
25903
|
var CLASS_RANK_DEFAULT = .25;
|
|
25904
25904
|
var PERSON_CLASSES = new Set(["person", "face"]);
|
|
25905
|
-
var VEHICLE_CLASSES = new Set([
|
|
25905
|
+
var VEHICLE_CLASSES$1 = new Set([
|
|
25906
25906
|
"vehicle",
|
|
25907
25907
|
"car",
|
|
25908
25908
|
"truck",
|
|
@@ -25933,7 +25933,7 @@ function clamp01$1(x) {
|
|
|
25933
25933
|
function classRank(className) {
|
|
25934
25934
|
const c = className.toLowerCase();
|
|
25935
25935
|
if (PERSON_CLASSES.has(c)) return 1;
|
|
25936
|
-
if (VEHICLE_CLASSES.has(c)) return CLASS_RANK_VEHICLE;
|
|
25936
|
+
if (VEHICLE_CLASSES$1.has(c)) return CLASS_RANK_VEHICLE;
|
|
25937
25937
|
if (ANIMAL_CLASSES.has(c)) return CLASS_RANK_ANIMAL;
|
|
25938
25938
|
return CLASS_RANK_DEFAULT;
|
|
25939
25939
|
}
|
|
@@ -26536,7 +26536,7 @@ var FaceGalleryProvider = class {
|
|
|
26536
26536
|
const identities = await this.identityStore.listIdentities();
|
|
26537
26537
|
const nameMap = new Map(identities.map((i) => [i.id, i.name]));
|
|
26538
26538
|
const identityName = face.recognizedIdentityId ? nameMap.get(face.recognizedIdentityId) : void 0;
|
|
26539
|
-
const
|
|
26539
|
+
const cropUrl = this.cropUrlFor(face);
|
|
26540
26540
|
return {
|
|
26541
26541
|
faceId: face.id,
|
|
26542
26542
|
deviceId: face.deviceId,
|
|
@@ -26545,7 +26545,7 @@ var FaceGalleryProvider = class {
|
|
|
26545
26545
|
...face.recognizedIdentityId != null ? { recognizedIdentityId: face.recognizedIdentityId } : {},
|
|
26546
26546
|
...identityName !== void 0 ? { identityName } : {},
|
|
26547
26547
|
assigned: face.assigned,
|
|
26548
|
-
...
|
|
26548
|
+
...cropUrl !== void 0 ? { cropUrl } : {},
|
|
26549
26549
|
...face.faceBbox !== void 0 ? { faceBbox: face.faceBbox } : {},
|
|
26550
26550
|
...face.keyFrameMediaKey !== void 0 ? { keyFrameMediaKey: face.keyFrameMediaKey } : {},
|
|
26551
26551
|
...face.bestMatchScore != null ? { bestMatchScore: face.bestMatchScore } : {},
|
|
@@ -29663,9 +29663,15 @@ var TrackCloser = class {
|
|
|
29663
29663
|
await this.suppressZeroMedia(t.deviceId, t.trackId);
|
|
29664
29664
|
return;
|
|
29665
29665
|
}
|
|
29666
|
-
|
|
29666
|
+
let thumbnailLanded = ownedMedia.some((m) => m.kind === "thumbnail");
|
|
29667
|
+
if (outcome === "raster-fallback" && closure?.rasterFallback) {
|
|
29668
|
+
await this.commitRasterFallback(t, closure.rasterFallback.jpeg, closure.rasterFallback.timestamp);
|
|
29669
|
+
thumbnailLanded = true;
|
|
29670
|
+
}
|
|
29667
29671
|
await this.maybePromoteLastFrame(t, ownedMedia);
|
|
29668
|
-
|
|
29672
|
+
const hasRasterFallback = closure?.rasterFallback !== void 0;
|
|
29673
|
+
if (!thumbnailLanded) thumbnailLanded = await this.maybeDeriveThumbnailFromKeyFrame(t, ownedMedia, hasRasterFallback);
|
|
29674
|
+
if (!thumbnailLanded && closure?.rasterFallback) await this.commitRasterFallback(t, closure.rasterFallback.jpeg, closure.rasterFallback.timestamp);
|
|
29669
29675
|
this.deps.logger.info("track ended", {
|
|
29670
29676
|
tags: { deviceId: t.deviceId },
|
|
29671
29677
|
meta: {
|
|
@@ -29783,14 +29789,23 @@ var TrackCloser = class {
|
|
|
29783
29789
|
* and the rate could only ever be counted from the database, after the fact.
|
|
29784
29790
|
* A branch that drops work silently reads as "never happened".
|
|
29785
29791
|
*/
|
|
29786
|
-
async maybeDeriveThumbnailFromKeyFrame(t, ownedMedia) {
|
|
29787
|
-
if (ownedMedia.some((m) => m.kind === "thumbnail")) return;
|
|
29792
|
+
async maybeDeriveThumbnailFromKeyFrame(t, ownedMedia, hasRasterFallback) {
|
|
29793
|
+
if (ownedMedia.some((m) => m.kind === "thumbnail")) return true;
|
|
29788
29794
|
const derive = this.deps.deriveThumbnailFromKeyFrame;
|
|
29789
|
-
if (!derive)
|
|
29795
|
+
if (!derive) {
|
|
29796
|
+
if (!hasRasterFallback) this.logNoThumbnailAtClose(t, "derive-not-wired");
|
|
29797
|
+
return false;
|
|
29798
|
+
}
|
|
29790
29799
|
const keyFrame = ownedMedia.find((m) => m.kind === "keyFrame");
|
|
29791
|
-
if (!keyFrame)
|
|
29800
|
+
if (!keyFrame) {
|
|
29801
|
+
if (!hasRasterFallback) this.logNoThumbnailAtClose(t, "no-key-frame");
|
|
29802
|
+
return false;
|
|
29803
|
+
}
|
|
29792
29804
|
const bestSeen = this.deps.residents.bestSeenBbox(t.trackId);
|
|
29793
|
-
if (!bestSeen)
|
|
29805
|
+
if (!bestSeen) {
|
|
29806
|
+
if (!hasRasterFallback) this.logNoThumbnailAtClose(t, "no-best-seen-bbox");
|
|
29807
|
+
return false;
|
|
29808
|
+
}
|
|
29794
29809
|
try {
|
|
29795
29810
|
const derived = await derive({
|
|
29796
29811
|
mediaKey: keyFrame.key,
|
|
@@ -29802,17 +29817,23 @@ var TrackCloser = class {
|
|
|
29802
29817
|
},
|
|
29803
29818
|
frameWidth: bestSeen.frameWidth,
|
|
29804
29819
|
frameHeight: bestSeen.frameHeight,
|
|
29805
|
-
timestamp:
|
|
29820
|
+
timestamp: keyFrame.timestamp
|
|
29806
29821
|
});
|
|
29807
|
-
if (!derived)
|
|
29822
|
+
if (!derived) {
|
|
29823
|
+
if (!hasRasterFallback) this.logNoThumbnailAtClose(t, "derive-returned-null", { keyFrameKey: keyFrame.key });
|
|
29824
|
+
return false;
|
|
29825
|
+
}
|
|
29808
29826
|
const mediaStore = this.deps.mediaStore();
|
|
29809
|
-
if (!mediaStore)
|
|
29827
|
+
if (!mediaStore) {
|
|
29828
|
+
if (!hasRasterFallback) this.logNoThumbnailAtClose(t, "no-media-store");
|
|
29829
|
+
return false;
|
|
29830
|
+
}
|
|
29810
29831
|
await mediaStore.put({
|
|
29811
29832
|
deviceId: t.deviceId,
|
|
29812
29833
|
ownerKind: "track",
|
|
29813
29834
|
ownerId: t.trackId,
|
|
29814
29835
|
kind: "thumbnail",
|
|
29815
|
-
timestamp:
|
|
29836
|
+
timestamp: keyFrame.timestamp,
|
|
29816
29837
|
data: derived.thumbnail
|
|
29817
29838
|
});
|
|
29818
29839
|
await mediaStore.put({
|
|
@@ -29820,7 +29841,7 @@ var TrackCloser = class {
|
|
|
29820
29841
|
ownerKind: "track",
|
|
29821
29842
|
ownerId: t.trackId,
|
|
29822
29843
|
kind: "thumbnailSmall",
|
|
29823
|
-
timestamp:
|
|
29844
|
+
timestamp: keyFrame.timestamp,
|
|
29824
29845
|
data: derived.thumbnailSmall
|
|
29825
29846
|
});
|
|
29826
29847
|
this.deps.logger.info("thumbnail derived from keyFrame at close (no live best-shot landed)", {
|
|
@@ -29831,6 +29852,7 @@ var TrackCloser = class {
|
|
|
29831
29852
|
skewMs: derived.skewMs
|
|
29832
29853
|
}
|
|
29833
29854
|
});
|
|
29855
|
+
return true;
|
|
29834
29856
|
} catch (err) {
|
|
29835
29857
|
this.deps.logger.debug("close-time keyFrame thumbnail derive failed", {
|
|
29836
29858
|
tags: { deviceId: t.deviceId },
|
|
@@ -29839,10 +29861,11 @@ var TrackCloser = class {
|
|
|
29839
29861
|
error: String(err)
|
|
29840
29862
|
}
|
|
29841
29863
|
});
|
|
29842
|
-
this.logNoThumbnailAtClose(t, "derive-threw", {
|
|
29864
|
+
if (!hasRasterFallback) this.logNoThumbnailAtClose(t, "derive-threw", {
|
|
29843
29865
|
keyFrameKey: keyFrame.key,
|
|
29844
29866
|
error: String(err)
|
|
29845
29867
|
});
|
|
29868
|
+
return false;
|
|
29846
29869
|
}
|
|
29847
29870
|
}
|
|
29848
29871
|
/**
|
|
@@ -30712,6 +30735,7 @@ var PlateGalleryProvider = class {
|
|
|
30712
30735
|
logger;
|
|
30713
30736
|
refreshGallery;
|
|
30714
30737
|
emitPlateGalleryChanged;
|
|
30738
|
+
eventMediaBaseUrl;
|
|
30715
30739
|
constructor(deps) {
|
|
30716
30740
|
this.plateStore = deps.plateStore;
|
|
30717
30741
|
this.vehicleStore = deps.vehicleStore;
|
|
@@ -30721,6 +30745,7 @@ var PlateGalleryProvider = class {
|
|
|
30721
30745
|
this.logger = deps.logger;
|
|
30722
30746
|
this.refreshGallery = deps.refreshGallery;
|
|
30723
30747
|
this.emitPlateGalleryChanged = deps.emitPlateGalleryChanged;
|
|
30748
|
+
this.eventMediaBaseUrl = deps.eventMediaBaseUrl;
|
|
30724
30749
|
}
|
|
30725
30750
|
/** Best-effort emit — never allowed to fail the mutation it follows. */
|
|
30726
30751
|
safeEmitPlateGalleryChanged(payload) {
|
|
@@ -30728,6 +30753,12 @@ var PlateGalleryProvider = class {
|
|
|
30728
30753
|
this.emitPlateGalleryChanged(payload);
|
|
30729
30754
|
} catch {}
|
|
30730
30755
|
}
|
|
30756
|
+
cropUrlFor(plate) {
|
|
30757
|
+
if (plate.mediaKey === void 0 || plate.mediaKey === "") return void 0;
|
|
30758
|
+
const base = this.eventMediaBaseUrl();
|
|
30759
|
+
if (base === null) return void 0;
|
|
30760
|
+
return `${base}/${encodeURIComponent(plate.mediaKey)}`;
|
|
30761
|
+
}
|
|
30731
30762
|
async plateCropBase64(plate) {
|
|
30732
30763
|
if (plate.mediaKey) {
|
|
30733
30764
|
const m = await this.mediaStore.getByKey(plate.mediaKey);
|
|
@@ -30736,8 +30767,9 @@ var PlateGalleryProvider = class {
|
|
|
30736
30767
|
const media = await this.mediaStore.listByOwner("plate", plate.id);
|
|
30737
30768
|
return media.length > 0 ? media[0].base64 : void 0;
|
|
30738
30769
|
}
|
|
30739
|
-
async toPlateInfo(plate, nameMap) {
|
|
30740
|
-
const base64 = await this.plateCropBase64(plate);
|
|
30770
|
+
async toPlateInfo(plate, nameMap, opts) {
|
|
30771
|
+
const base64 = opts?.includeCrops ?? true ? await this.plateCropBase64(plate) : void 0;
|
|
30772
|
+
const cropUrl = this.cropUrlFor(plate);
|
|
30741
30773
|
const vehicleName = plate.recognizedVehicleId != null ? nameMap?.get(plate.recognizedVehicleId) : void 0;
|
|
30742
30774
|
return {
|
|
30743
30775
|
plateId: plate.id,
|
|
@@ -30752,7 +30784,8 @@ var PlateGalleryProvider = class {
|
|
|
30752
30784
|
...vehicleName !== void 0 ? { vehicleName } : {},
|
|
30753
30785
|
...plate.plateBbox !== void 0 ? { plateBbox: plate.plateBbox } : {},
|
|
30754
30786
|
...plate.keyFrameMediaKey !== void 0 ? { keyFrameMediaKey: plate.keyFrameMediaKey } : {},
|
|
30755
|
-
...base64 !== void 0 ? { base64 } : {}
|
|
30787
|
+
...base64 !== void 0 ? { base64 } : {},
|
|
30788
|
+
...cropUrl !== void 0 ? { cropUrl } : {}
|
|
30756
30789
|
};
|
|
30757
30790
|
}
|
|
30758
30791
|
async vehicleNameMap() {
|
|
@@ -30771,7 +30804,7 @@ var PlateGalleryProvider = class {
|
|
|
30771
30804
|
const plate = await this.plateStore.get(`plate-${input.trackId}`);
|
|
30772
30805
|
if (!plate || plate.deviceId !== input.deviceId) return null;
|
|
30773
30806
|
const nameMap = await this.vehicleNameMap();
|
|
30774
|
-
return this.toPlateInfo(plate, nameMap);
|
|
30807
|
+
return this.toPlateInfo(plate, nameMap, { includeCrops: false });
|
|
30775
30808
|
}
|
|
30776
30809
|
async getPlateMedia(input) {
|
|
30777
30810
|
return (await this.mediaStore.listByOwner("plate", input.plateId)).map((m) => ({
|
|
@@ -35874,6 +35907,19 @@ function createEventMediaHandler(deps) {
|
|
|
35874
35907
|
};
|
|
35875
35908
|
}
|
|
35876
35909
|
//#endregion
|
|
35910
|
+
//#region src/pipeline-analytics/copy-face-crop-preview.ts
|
|
35911
|
+
var TRACK_PREVIEW_KINDS = new Set(["thumbnail", "thumbnailSmall"]);
|
|
35912
|
+
async function copyFaceCropIfTrackHasNoThumbnail(store, input) {
|
|
35913
|
+
if ((await store.listTrackMedia(input.trackId)).some((m) => TRACK_PREVIEW_KINDS.has(m.kind))) return false;
|
|
35914
|
+
await store.putTrackThumbnail({
|
|
35915
|
+
deviceId: input.deviceId,
|
|
35916
|
+
trackId: input.trackId,
|
|
35917
|
+
timestamp: input.timestamp,
|
|
35918
|
+
data: input.crop
|
|
35919
|
+
});
|
|
35920
|
+
return true;
|
|
35921
|
+
}
|
|
35922
|
+
//#endregion
|
|
35877
35923
|
//#region src/pipeline-analytics/event-thumbnail-resolver.ts
|
|
35878
35924
|
/** Pick an event-OWNED media blob: the tight `crop`, else the native-res boxed
|
|
35879
35925
|
* `fullFrameBoxed`, else any available file. Both preferred kinds are RETIRED
|
|
@@ -35882,29 +35928,47 @@ function createEventMediaHandler(deps) {
|
|
|
35882
35928
|
function pickEventOwnedMedia(files) {
|
|
35883
35929
|
return files.find((f) => f.kind === "crop") ?? files.find((f) => f.kind === "fullFrameBoxed") ?? files[0];
|
|
35884
35930
|
}
|
|
35885
|
-
/** Pick a track's fallback media
|
|
35886
|
-
*
|
|
35887
|
-
*
|
|
35888
|
-
* Undefined when the
|
|
35931
|
+
/** Pick a track's fallback media when no subject crop exists: `thumbnailSmall`
|
|
35932
|
+
* → `thumbnail` → native `keyFrameSmall`/`keyFrame`/`fullFrame` → rolling
|
|
35933
|
+
* `lastFrame` → `firstFrame` → newest `snapshot` → any blob. A full frame
|
|
35934
|
+
* beats a class icon. Undefined when the owner has no media. */
|
|
35889
35935
|
function pickTrackFallbackMedia(files) {
|
|
35890
|
-
return files.find((f) => f.kind === "thumbnailSmall") ?? files.find((f) => f.kind === "thumbnail") ?? files.find((f) => f.kind === "lastFrame") ?? files.find((f) => f.kind === "firstFrame") ?? [...files].reverse().find((f) => f.kind === "snapshot") ?? files[files.length - 1];
|
|
35936
|
+
return files.find((f) => f.kind === "thumbnailSmall") ?? files.find((f) => f.kind === "thumbnail") ?? files.find((f) => f.kind === "keyFrameSmall") ?? files.find((f) => f.kind === "keyFrame") ?? files.find((f) => f.kind === "fullFrame") ?? files.find((f) => f.kind === "lastFrame") ?? files.find((f) => f.kind === "firstFrame") ?? [...files].reverse().find((f) => f.kind === "snapshot") ?? files[files.length - 1];
|
|
35891
35937
|
}
|
|
35892
35938
|
/**
|
|
35893
|
-
* Crop-forced resolution (the `?kind=crop`
|
|
35894
|
-
*
|
|
35895
|
-
*
|
|
35896
|
-
*
|
|
35897
|
-
*
|
|
35898
|
-
* returning undefined. A boxed frame (with the target's box drawn) is a real
|
|
35899
|
-
* preview; an empty tile is not. Clean is always tried FIRST so the common case
|
|
35900
|
-
* still serves an unboxed tile. Undefined only when the owner has NO media.
|
|
35939
|
+
* Crop-forced resolution (the `?kind=crop` / `?kind=thumbnailSmall` reel path).
|
|
35940
|
+
*
|
|
35941
|
+
* Ladder: body crop (track/event thumbnail) → tight face/plate crop → any
|
|
35942
|
+
* remaining image (keyFrame / lastFrame / …). The class icon is only honest
|
|
35943
|
+
* when the owner has no stored bytes at all.
|
|
35901
35944
|
*
|
|
35902
35945
|
* `pickClean` is injected because the clean-kind picker (`pickCleanMedia`) lives
|
|
35903
35946
|
* in the addon `index.ts` over the concrete `MediaFileKind` set; this keeps the
|
|
35904
35947
|
* composition pure + unit-testable here.
|
|
35905
35948
|
*/
|
|
35906
|
-
function resolveCropForcedMedia(files, pickClean) {
|
|
35907
|
-
return pickClean(files) ?? pickTrackFallbackMedia(files);
|
|
35949
|
+
function resolveCropForcedMedia(files, pickClean, detailFiles = []) {
|
|
35950
|
+
return pickClean(files) ?? pickClean(detailFiles) ?? pickTrackFallbackMedia([...files, ...detailFiles]);
|
|
35951
|
+
}
|
|
35952
|
+
/**
|
|
35953
|
+
* Event + track media (the body/scene) and face/plate media (the detail
|
|
35954
|
+
* crops). Face rows live under `face-${trackId}`; a track can close with
|
|
35955
|
+
* `hasEmbeddedFace` and ZERO track files (0e343f86).
|
|
35956
|
+
*/
|
|
35957
|
+
async function collectCropForcedMedia(source, id) {
|
|
35958
|
+
const eventFiles = await source.listByOwner("event", id);
|
|
35959
|
+
const trackFiles = await source.listByOwner("track", id);
|
|
35960
|
+
const ownerTrackId = trackFiles.length === 0 ? await source.getTrackIdForEvent(id) : null;
|
|
35961
|
+
const owningTrackFiles = ownerTrackId === null ? [] : await source.listByOwner("track", ownerTrackId);
|
|
35962
|
+
const trackId = ownerTrackId ?? id;
|
|
35963
|
+
const [faceFiles, plateFiles] = await Promise.all([source.listByOwner("face", `face-${trackId}`), source.listByOwner("plate", `plate-${trackId}`)]);
|
|
35964
|
+
return {
|
|
35965
|
+
bodyFiles: [
|
|
35966
|
+
...eventFiles,
|
|
35967
|
+
...trackFiles,
|
|
35968
|
+
...owningTrackFiles
|
|
35969
|
+
],
|
|
35970
|
+
detailFiles: [...faceFiles, ...plateFiles]
|
|
35971
|
+
};
|
|
35908
35972
|
}
|
|
35909
35973
|
/**
|
|
35910
35974
|
* Resolve the default-path media for an event id: the event's own media when it
|
|
@@ -35918,6 +35982,14 @@ async function resolveDefaultEventMedia(deps) {
|
|
|
35918
35982
|
return pickTrackFallbackMedia(await deps.listTrackMedia(trackId)) ?? null;
|
|
35919
35983
|
}
|
|
35920
35984
|
//#endregion
|
|
35985
|
+
//#region src/pipeline-analytics/ensure-track-hierarchy.ts
|
|
35986
|
+
async function ensureTrackHierarchyMedia(deps, input) {
|
|
35987
|
+
const tasks = [];
|
|
35988
|
+
if (!deps.isThumbnailLanded(input.trackId)) tasks.push(deps.captureBodyThumbnail(input));
|
|
35989
|
+
if (!deps.isKeyFrameLanded(input.trackId)) tasks.push(deps.captureKeyFrame(input));
|
|
35990
|
+
await Promise.all(tasks);
|
|
35991
|
+
}
|
|
35992
|
+
//#endregion
|
|
35921
35993
|
//#region src/pipeline-analytics/face-settings.ts
|
|
35922
35994
|
/**
|
|
35923
35995
|
* Per-device face-recognition settings. Cascade: a per-device override on top
|
|
@@ -36436,6 +36508,21 @@ var FaceRecognizer = class {
|
|
|
36436
36508
|
...held,
|
|
36437
36509
|
crop
|
|
36438
36510
|
});
|
|
36511
|
+
if (crop !== void 0 && input.frameHandle !== void 0) this.deps.onLiveHierarchy?.({
|
|
36512
|
+
deviceId: input.deviceId,
|
|
36513
|
+
trackId: c.trackId,
|
|
36514
|
+
timestamp: input.timestamp,
|
|
36515
|
+
bodyBbox: {
|
|
36516
|
+
x: c.bbox.x,
|
|
36517
|
+
y: c.bbox.y,
|
|
36518
|
+
w: c.bbox.w,
|
|
36519
|
+
h: c.bbox.h
|
|
36520
|
+
},
|
|
36521
|
+
frameWidth: input.frameWidth,
|
|
36522
|
+
frameHeight: input.frameHeight,
|
|
36523
|
+
cropPadding: input.cropPadding,
|
|
36524
|
+
frameHandle: input.frameHandle
|
|
36525
|
+
});
|
|
36439
36526
|
this.deps.logger.debug("face: best-face held", {
|
|
36440
36527
|
tags: {
|
|
36441
36528
|
deviceId: input.deviceId,
|
|
@@ -36585,6 +36672,22 @@ var FaceRecognizer = class {
|
|
|
36585
36672
|
});
|
|
36586
36673
|
return;
|
|
36587
36674
|
}
|
|
36675
|
+
try {
|
|
36676
|
+
await this.deps.copyCropToTrackPreview?.({
|
|
36677
|
+
deviceId,
|
|
36678
|
+
trackId,
|
|
36679
|
+
crop: held.crop,
|
|
36680
|
+
timestamp: held.timestamp
|
|
36681
|
+
});
|
|
36682
|
+
} catch (err) {
|
|
36683
|
+
this.deps.logger.debug("face: track preview copy failed", {
|
|
36684
|
+
tags: { deviceId },
|
|
36685
|
+
meta: {
|
|
36686
|
+
trackId,
|
|
36687
|
+
error: String(err)
|
|
36688
|
+
}
|
|
36689
|
+
});
|
|
36690
|
+
}
|
|
36588
36691
|
const keyFrameMediaKey = this.deps.getKeyFrameMediaKey?.(trackId);
|
|
36589
36692
|
try {
|
|
36590
36693
|
await this.deps.faceStore.insert({
|
|
@@ -44164,6 +44267,12 @@ var PlateRecognizer = class {
|
|
|
44164
44267
|
text: c.plateText,
|
|
44165
44268
|
score: c.plateScore,
|
|
44166
44269
|
bbox: c.plateBbox,
|
|
44270
|
+
bodyBbox: {
|
|
44271
|
+
x: c.bbox.x,
|
|
44272
|
+
y: c.bbox.y,
|
|
44273
|
+
w: c.bbox.w,
|
|
44274
|
+
h: c.bbox.h
|
|
44275
|
+
},
|
|
44167
44276
|
timestamp: input.timestamp,
|
|
44168
44277
|
frameWidth: input.frameWidth,
|
|
44169
44278
|
frameHeight: input.frameHeight,
|
|
@@ -44237,6 +44346,16 @@ var PlateRecognizer = class {
|
|
|
44237
44346
|
}
|
|
44238
44347
|
});
|
|
44239
44348
|
}
|
|
44349
|
+
if (crop !== void 0 && input.frameHandle !== void 0 && input.bodyBbox !== void 0) this.deps.onLiveHierarchy?.({
|
|
44350
|
+
deviceId: input.deviceId,
|
|
44351
|
+
trackId: input.trackId,
|
|
44352
|
+
timestamp: input.timestamp,
|
|
44353
|
+
bodyBbox: input.bodyBbox,
|
|
44354
|
+
frameWidth: input.frameWidth,
|
|
44355
|
+
frameHeight: input.frameHeight,
|
|
44356
|
+
cropPadding: input.cropPadding,
|
|
44357
|
+
frameHandle: input.frameHandle
|
|
44358
|
+
});
|
|
44240
44359
|
this.bestPlate.set(input.trackId, {
|
|
44241
44360
|
text: input.text,
|
|
44242
44361
|
score: input.score,
|
|
@@ -48271,14 +48390,23 @@ function isAfterCursor(row, cursor) {
|
|
|
48271
48390
|
if (row.lastSeen < cursor.lastSeen) return true;
|
|
48272
48391
|
return row.lastSeen === cursor.lastSeen && row.id < cursor.trackId;
|
|
48273
48392
|
}
|
|
48393
|
+
var VEHICLE_CLASSES = new Set([
|
|
48394
|
+
"vehicle",
|
|
48395
|
+
"car",
|
|
48396
|
+
"truck",
|
|
48397
|
+
"bus"
|
|
48398
|
+
]);
|
|
48399
|
+
function motionlessMaxPxForClass(className) {
|
|
48400
|
+
return VEHICLE_CLASSES.has(className.toLowerCase()) ? 8 : 40;
|
|
48401
|
+
}
|
|
48274
48402
|
function netDisplacementPx(positions) {
|
|
48275
48403
|
const first = positions[0];
|
|
48276
48404
|
const last = positions[positions.length - 1];
|
|
48277
48405
|
if (!first || !last) return 0;
|
|
48278
48406
|
return Math.hypot(last.x - first.x, last.y - first.y);
|
|
48279
48407
|
}
|
|
48280
|
-
function isShortMotionlessTrack(lifeMs, movedPx) {
|
|
48281
|
-
return lifeMs <= 25e3 && movedPx <=
|
|
48408
|
+
function isShortMotionlessTrack(lifeMs, movedPx, className) {
|
|
48409
|
+
return lifeMs <= 25e3 && movedPx <= motionlessMaxPxForClass(className);
|
|
48282
48410
|
}
|
|
48283
48411
|
//#endregion
|
|
48284
48412
|
//#region src/pipeline-analytics/store/track-store.ts
|
|
@@ -48862,7 +48990,7 @@ var TrackStore = class {
|
|
|
48862
48990
|
for (const [trackId, t] of this.active) {
|
|
48863
48991
|
if (nowMs - t.lastSeen < this.config.ttlMs) continue;
|
|
48864
48992
|
t.active = false;
|
|
48865
|
-
if (t.positions.length > 0 && isShortMotionlessTrack(t.lastSeen - t.firstSeen, netDisplacementPx(t.positions))) t.closedReason = "stationary-promotion";
|
|
48993
|
+
if (t.positions.length > 0 && isShortMotionlessTrack(t.lastSeen - t.firstSeen, netDisplacementPx(t.positions), t.className)) t.closedReason = "stationary-promotion";
|
|
48866
48994
|
const record = cloneTrack(t);
|
|
48867
48995
|
try {
|
|
48868
48996
|
await this.persistCompleted(record);
|
|
@@ -56868,10 +56996,12 @@ var CONFIRMATION_CROP_MAX_WIDTH = 320;
|
|
|
56868
56996
|
* a late confirmation is still worth running; see `DEFAULT_CAPTURE_PRIORITY`). */
|
|
56869
56997
|
var ENRICHMENT_CAPTURE_STALE_MS = 3e3;
|
|
56870
56998
|
/** Reconcile cadence for the per-device `zones` / `zoneRules` slice handles.
|
|
56871
|
-
* The `device.state-changed` push
|
|
56872
|
-
*
|
|
56873
|
-
*
|
|
56874
|
-
|
|
56999
|
+
* The `device.state-changed` push reaches a forked child through
|
|
57000
|
+
* `createEventBusSliceSource` (the DeviceProxy no longer waits on
|
|
57001
|
+
* `live.onEvent`). Events are still lossy telemetry (D8); this timer
|
|
57002
|
+
* is the RPC reconcile, not the mechanism. Five minutes matches the
|
|
57003
|
+
* viewer's `RECONCILE_MS`. */
|
|
57004
|
+
var ZONE_SLICE_RECONCILE_MS = 3e5;
|
|
56875
57005
|
/** How often the evaluating node re-reads the central scene set. */
|
|
56876
57006
|
var SCENE_RELOAD_INTERVAL_MS = 3e4;
|
|
56877
57007
|
/** How long the install coordinates are trusted between settings reads. */
|
|
@@ -56935,38 +57065,31 @@ var MOTION_EVENT_HEARTBEAT_MS = 5e3;
|
|
|
56935
57065
|
*
|
|
56936
57066
|
* `thumbnail` joined the clean set on 2026-07-17: the track best-thumbnail is
|
|
56937
57067
|
* now a subject-centered crop (event-media-dispatcher), and for DEGENERATE
|
|
56938
|
-
* tracks (no object event → no `crop`) it is the ONLY subject-centered media
|
|
56939
|
-
*
|
|
56940
|
-
*
|
|
56941
|
-
*
|
|
56942
|
-
*
|
|
56943
|
-
*
|
|
56944
|
-
*
|
|
56945
|
-
*
|
|
56946
|
-
*
|
|
56947
|
-
* over 404). The retired-but-still-stored kinds (`crop`) stay listed so old rows
|
|
56948
|
-
* keep resolving.
|
|
57068
|
+
* tracks (no object event → no `crop`) it is the ONLY subject-centered media.
|
|
57069
|
+
*
|
|
57070
|
+
* Full-scene kinds (`fullFrame`, `keyFrame`, `keyFrameSmall`) stay out of
|
|
57071
|
+
* this list so a crop is preferred when one exists. `resolveCropForcedMedia`
|
|
57072
|
+
* then falls through to those full-scene kinds — a keyFrame beats an icon.
|
|
57073
|
+
* An *explicit* `?kind=keyFrame` / `?kind=snapshot` still matches exactly, so
|
|
57074
|
+
* videoclips and other kind-specific fetches keep working.
|
|
57075
|
+
*
|
|
57076
|
+
* The retired-but-still-stored kind (`crop`) stays listed so old rows resolve.
|
|
56949
57077
|
*/
|
|
56950
57078
|
var CLEAN_MEDIA_KINDS = [
|
|
56951
57079
|
"crop",
|
|
56952
57080
|
"thumbnail",
|
|
56953
|
-
"thumbnailSmall"
|
|
56954
|
-
"fullFrame",
|
|
56955
|
-
"keyFrame",
|
|
56956
|
-
"keyFrameSmall"
|
|
57081
|
+
"thumbnailSmall"
|
|
56957
57082
|
];
|
|
56958
57083
|
/**
|
|
56959
|
-
* Pick
|
|
56960
|
-
* `
|
|
56961
|
-
* {@link CLEAN_MEDIA_KINDS}
|
|
56962
|
-
*
|
|
57084
|
+
* Pick media for a crop-forced request: the exact `preferKind` when present
|
|
57085
|
+
* (so `?kind=snapshot` still serves a snapshot), else the first available
|
|
57086
|
+
* subject crop in {@link CLEAN_MEDIA_KINDS}. Returns undefined when no crop
|
|
57087
|
+
* is in the list — the caller then falls through to any remaining image
|
|
57088
|
+
* (`resolveCropForcedMedia`); the class icon is only for a total miss.
|
|
56963
57089
|
*/
|
|
56964
57090
|
function pickCleanMedia(files, preferKind) {
|
|
56965
|
-
const
|
|
56966
|
-
if (
|
|
56967
|
-
const exact = files.find((f) => f.kind === preferKind);
|
|
56968
|
-
if (exact) return exact;
|
|
56969
|
-
}
|
|
57091
|
+
const exact = files.find((f) => f.kind === preferKind);
|
|
57092
|
+
if (exact) return exact;
|
|
56970
57093
|
for (const kind of CLEAN_MEDIA_KINDS) {
|
|
56971
57094
|
const found = files.find((f) => f.kind === kind);
|
|
56972
57095
|
if (found) return found;
|
|
@@ -58430,6 +58553,19 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
58430
58553
|
}, trackId);
|
|
58431
58554
|
},
|
|
58432
58555
|
markEmbeddedFace: (trackId) => stores.trackStore.markEmbeddedFace(trackId),
|
|
58556
|
+
copyCropToTrackPreview: async (input) => {
|
|
58557
|
+
await copyFaceCropIfTrackHasNoThumbnail({
|
|
58558
|
+
listTrackMedia: (trackId) => stores.mediaStore.listByOwner("track", trackId),
|
|
58559
|
+
putTrackThumbnail: (put) => stores.mediaStore.put({
|
|
58560
|
+
deviceId: put.deviceId,
|
|
58561
|
+
ownerKind: "track",
|
|
58562
|
+
ownerId: put.trackId,
|
|
58563
|
+
kind: "thumbnail",
|
|
58564
|
+
timestamp: put.timestamp,
|
|
58565
|
+
data: put.data
|
|
58566
|
+
})
|
|
58567
|
+
}, input);
|
|
58568
|
+
},
|
|
58433
58569
|
getKeyFrameMediaKey: (trackId) => this.residents.keyFrameKey(trackId),
|
|
58434
58570
|
emitFaceGalleryChanged: (payload) => this.emitFaceGalleryChanged(payload),
|
|
58435
58571
|
onSemanticMatch: (match) => this.promoteSemanticRecognition({
|
|
@@ -58444,6 +58580,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
58444
58580
|
confidence: match.confidence,
|
|
58445
58581
|
...match.frameHandle !== void 0 ? { frameHandle: match.frameHandle } : {}
|
|
58446
58582
|
}),
|
|
58583
|
+
onLiveHierarchy: (input) => this.requestTrackHierarchy(input),
|
|
58447
58584
|
logger: logger.child("FaceRecognizer")
|
|
58448
58585
|
});
|
|
58449
58586
|
this.faceRecognizer.refreshGallery();
|
|
@@ -58476,6 +58613,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
58476
58613
|
deriveCropFromKeyFrame,
|
|
58477
58614
|
getKeyFrameMediaKey: (trackId) => this.residents.keyFrameKey(trackId),
|
|
58478
58615
|
emitPlateGalleryChanged: (payload) => this.emitPlateGalleryChanged(payload),
|
|
58616
|
+
onLiveHierarchy: (input) => this.requestTrackHierarchy(input),
|
|
58479
58617
|
logger: logger.child("PlateRecognizer")
|
|
58480
58618
|
});
|
|
58481
58619
|
this.plateRecognizer.refreshGallery();
|
|
@@ -59845,6 +59983,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
59845
59983
|
refreshGallery: () => {
|
|
59846
59984
|
this.plateRecognizer?.refreshGallery();
|
|
59847
59985
|
},
|
|
59986
|
+
eventMediaBaseUrl: () => this.eventMediaBaseUrl,
|
|
59848
59987
|
emitPlateGalleryChanged: (payload) => this.emitPlateGalleryChanged(payload)
|
|
59849
59988
|
});
|
|
59850
59989
|
return [
|
|
@@ -60901,7 +61040,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
60901
61040
|
if (!first) return;
|
|
60902
61041
|
const lifeMs = track.lastSeen - track.firstSeen;
|
|
60903
61042
|
const moved = netDisplacementPx(track.positions ?? []);
|
|
60904
|
-
if (!isShortMotionlessTrack(lifeMs, moved)) {
|
|
61043
|
+
if (!isShortMotionlessTrack(lifeMs, moved, track.className)) {
|
|
60905
61044
|
this.phantomCells?.noteMovingClose({
|
|
60906
61045
|
deviceId: track.deviceId,
|
|
60907
61046
|
className: track.className,
|
|
@@ -61019,6 +61158,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
61019
61158
|
}, d.score, d.label, frame.timestamp);
|
|
61020
61159
|
if (this.plateRecognizer) {
|
|
61021
61160
|
const mediaSettings = await this.resolveDeviceMediaSettings(deviceId);
|
|
61161
|
+
const body = this.residents.bestSeenBbox(trackId);
|
|
61022
61162
|
await this.plateRecognizer.observePlateRead({
|
|
61023
61163
|
deviceId,
|
|
61024
61164
|
trackId,
|
|
@@ -61034,7 +61174,13 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
61034
61174
|
frameWidth: frame.frameWidth,
|
|
61035
61175
|
frameHeight: frame.frameHeight,
|
|
61036
61176
|
cropPadding: mediaSettings.cropPadding,
|
|
61037
|
-
...frame.frameHandle !== void 0 ? { frameHandle: frame.frameHandle } : {}
|
|
61177
|
+
...frame.frameHandle !== void 0 ? { frameHandle: frame.frameHandle } : {},
|
|
61178
|
+
...body !== void 0 ? { bodyBbox: {
|
|
61179
|
+
x: body.x,
|
|
61180
|
+
y: body.y,
|
|
61181
|
+
w: body.w,
|
|
61182
|
+
h: body.h
|
|
61183
|
+
} } : {}
|
|
61038
61184
|
});
|
|
61039
61185
|
}
|
|
61040
61186
|
}
|
|
@@ -61335,6 +61481,51 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
61335
61481
|
}
|
|
61336
61482
|
}).catch(() => {})));
|
|
61337
61483
|
}
|
|
61484
|
+
/**
|
|
61485
|
+
* A face or plate crop just landed from a still-live handle. Fill the
|
|
61486
|
+
* missing parent rungs — person/vehicle thumbnail (display crop, same
|
|
61487
|
+
* surface that produced the detail) and native keyFrame — so Events never
|
|
61488
|
+
* has a child crop without a body and a scene.
|
|
61489
|
+
*/
|
|
61490
|
+
requestTrackHierarchy(input) {
|
|
61491
|
+
ensureTrackHierarchyMedia({
|
|
61492
|
+
isThumbnailLanded: (trackId) => this.residents.isThumbnailLanded(trackId),
|
|
61493
|
+
isKeyFrameLanded: (trackId) => this.residents.keyFrameCapture(trackId).landed,
|
|
61494
|
+
captureBodyThumbnail: (req) => this.captureHierarchyBodyThumbnail(req),
|
|
61495
|
+
captureKeyFrame: async (req) => {
|
|
61496
|
+
await this.persistKeyFrames(req.deviceId, req.timestamp, [{
|
|
61497
|
+
trackId: req.trackId,
|
|
61498
|
+
retry: false
|
|
61499
|
+
}], req.frameHandle);
|
|
61500
|
+
}
|
|
61501
|
+
}, input);
|
|
61502
|
+
}
|
|
61503
|
+
async captureHierarchyBodyThumbnail(input) {
|
|
61504
|
+
const displayCrop = this.captureDisplayCropFn;
|
|
61505
|
+
const mediaStore = this.mediaStore;
|
|
61506
|
+
if (!displayCrop || !mediaStore) return;
|
|
61507
|
+
await this.captureScheduler.request({
|
|
61508
|
+
deviceId: input.deviceId,
|
|
61509
|
+
trackId: input.trackId,
|
|
61510
|
+
kind: "thumbnail",
|
|
61511
|
+
coalesceKey: captureCoalesceKey(input.deviceId, "thumbnail", `${input.trackId}:hierarchy`),
|
|
61512
|
+
staleAfterMs: ENRICHMENT_CAPTURE_STALE_MS,
|
|
61513
|
+
exec: async () => {
|
|
61514
|
+
if (this.residents.isThumbnailLanded(input.trackId)) return;
|
|
61515
|
+
const jpeg = await displayCrop(input.frameHandle, input.bodyBbox, input.frameWidth, input.frameHeight, input.cropPadding, input.trackId);
|
|
61516
|
+
if (!jpeg) return;
|
|
61517
|
+
await mediaStore.put({
|
|
61518
|
+
deviceId: input.deviceId,
|
|
61519
|
+
ownerKind: "track",
|
|
61520
|
+
ownerId: input.trackId,
|
|
61521
|
+
kind: "thumbnail",
|
|
61522
|
+
timestamp: input.timestamp,
|
|
61523
|
+
data: jpeg
|
|
61524
|
+
});
|
|
61525
|
+
this.residents.markThumbnailLanded(input.deviceId, input.trackId);
|
|
61526
|
+
}
|
|
61527
|
+
}).catch(() => {});
|
|
61528
|
+
}
|
|
61338
61529
|
/** Attribution-time SEMANTIC best-shot promotion (D-semantic-bestshot,
|
|
61339
61530
|
* 2026-08-21; see `pipeline/semantic-promotion.ts`): offer a frame from
|
|
61340
61531
|
* which a recognition SUCCEEDED to the best-frame ranking with the
|
|
@@ -62840,15 +63031,11 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
62840
63031
|
* applies to the very next frame even when frames stop briefly
|
|
62841
63032
|
* (e.g. during binding flips).
|
|
62842
63033
|
*
|
|
62843
|
-
* RECONCILE: the push channel behind `subscribe`
|
|
62844
|
-
* via `
|
|
62845
|
-
*
|
|
62846
|
-
*
|
|
62847
|
-
*
|
|
62848
|
-
* telemetry (D8); the durable channel is RPC + reconcile — so each
|
|
62849
|
-
* proxy also refreshes its two slices on a slow timer. `refresh()`
|
|
62850
|
-
* round-trips `deviceState.getCapSlice` and fans out through the SAME
|
|
62851
|
-
* subscribe callbacks above, so a zone edit lands within one interval.
|
|
63034
|
+
* RECONCILE: the push channel behind `subscribe` is `device.state-changed`
|
|
63035
|
+
* via the addon's `ctx.eventBus` (`createEventBusSliceSource`). That
|
|
63036
|
+
* declares UDS interest so the parent actually fans the slice here. Events
|
|
63037
|
+
* remain lossy (D8); `refresh()` round-trips `deviceState.getCapSlice` on
|
|
63038
|
+
* this timer as the backstop.
|
|
62852
63039
|
*/
|
|
62853
63040
|
async ensureProxy(deviceId) {
|
|
62854
63041
|
const cached = this.proxies.get(deviceId);
|
|
@@ -64779,14 +64966,11 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
64779
64966
|
async readEventThumbnail(id, preferKind) {
|
|
64780
64967
|
const eventFiles = await (this.mediaStore?.listByOwner("event", id) ?? Promise.resolve([]));
|
|
64781
64968
|
if (preferKind !== void 0 && preferKind.length > 0) {
|
|
64782
|
-
const
|
|
64783
|
-
|
|
64784
|
-
|
|
64785
|
-
|
|
64786
|
-
|
|
64787
|
-
...trackFiles,
|
|
64788
|
-
...owningTrackFiles
|
|
64789
|
-
], (files) => pickCleanMedia(files, preferKind));
|
|
64969
|
+
const layers = await collectCropForcedMedia({
|
|
64970
|
+
listByOwner: (ownerKind, ownerId) => this.mediaStore?.listByOwner(ownerKind, ownerId) ?? Promise.resolve([]),
|
|
64971
|
+
getTrackIdForEvent: (eventId) => this.eventStore?.getTrackIdForEvent(eventId) ?? Promise.resolve(null)
|
|
64972
|
+
}, id);
|
|
64973
|
+
const chosen = resolveCropForcedMedia(layers.bodyFiles, (files) => pickCleanMedia(files, preferKind), layers.detailFiles);
|
|
64790
64974
|
if (!chosen) return null;
|
|
64791
64975
|
return {
|
|
64792
64976
|
bytes: Buffer.from(chosen.base64, "base64"),
|
|
@@ -30,7 +30,7 @@ async function d(e) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async function f() {
|
|
33
|
-
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BYzwylnw.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|