@camstack/addon-post-analysis 1.2.182 → 1.2.184
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-_fedHYU3.js → dist-BvgxEznC.js} +21 -0
- package/dist/{dist-DvmLRRIq.mjs → dist-CNIOT48v.mjs} +21 -0
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BirG5etn.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CTTAduHj.mjs} +3 -3
- package/dist/pipeline-analytics/{_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DUEY5dJj.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DyspPDYM.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-DZG5yDTK.mjs → hostInit-bowBKBWa.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +379 -39
- package/dist/pipeline-analytics/index.mjs +379 -39
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-BvgxEznC.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_dist.__toESM(node_fs, 1);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -27835,6 +27835,7 @@ var StoragePressureTracker = class {
|
|
|
27835
27835
|
var OWNER_KINDS = [
|
|
27836
27836
|
"event",
|
|
27837
27837
|
"track",
|
|
27838
|
+
"summary",
|
|
27838
27839
|
"face",
|
|
27839
27840
|
"identity",
|
|
27840
27841
|
"plate",
|
|
@@ -28418,7 +28419,8 @@ var MediaStore = class {
|
|
|
28418
28419
|
relativePath: path
|
|
28419
28420
|
})).toString("base64"),
|
|
28420
28421
|
sizeBytes,
|
|
28421
|
-
timestamp
|
|
28422
|
+
timestamp,
|
|
28423
|
+
deviceId: Number(data["deviceId"])
|
|
28422
28424
|
};
|
|
28423
28425
|
} catch {
|
|
28424
28426
|
return null;
|
|
@@ -30693,7 +30695,7 @@ async function mapWithConcurrency(items, limit, mapper) {
|
|
|
30693
30695
|
* all-signals-maxed track scores exactly 1.0 (the clamp is then a no-op) and an
|
|
30694
30696
|
* all-zero track scores 0.
|
|
30695
30697
|
*/
|
|
30696
|
-
var WEIGHT_CONFIDENCE = .3;
|
|
30698
|
+
var WEIGHT_CONFIDENCE$1 = .3;
|
|
30697
30699
|
var WEIGHT_CLASS = .2;
|
|
30698
30700
|
var WEIGHT_DWELL = .2;
|
|
30699
30701
|
var WEIGHT_PROXIMITY = .12;
|
|
@@ -30731,7 +30733,7 @@ var ANIMAL_CLASSES = new Set([
|
|
|
30731
30733
|
"cat"
|
|
30732
30734
|
]);
|
|
30733
30735
|
/** Clamp to [0,1]. */
|
|
30734
|
-
function clamp01$
|
|
30736
|
+
function clamp01$2(x) {
|
|
30735
30737
|
if (Number.isNaN(x)) return 0;
|
|
30736
30738
|
if (x < 0) return 0;
|
|
30737
30739
|
if (x > 1) return 1;
|
|
@@ -30765,7 +30767,7 @@ function computeImportance(input) {
|
|
|
30765
30767
|
const terms = [
|
|
30766
30768
|
{
|
|
30767
30769
|
reason: "confidence",
|
|
30768
|
-
value: WEIGHT_CONFIDENCE * clamp01$
|
|
30770
|
+
value: WEIGHT_CONFIDENCE$1 * clamp01$2(input.peakConfidence)
|
|
30769
30771
|
},
|
|
30770
30772
|
{
|
|
30771
30773
|
reason: "class",
|
|
@@ -30773,15 +30775,15 @@ function computeImportance(input) {
|
|
|
30773
30775
|
},
|
|
30774
30776
|
{
|
|
30775
30777
|
reason: "dwell",
|
|
30776
|
-
value: WEIGHT_DWELL * clamp01$
|
|
30778
|
+
value: WEIGHT_DWELL * clamp01$2(input.durationMs / DWELL_FULL_MS)
|
|
30777
30779
|
},
|
|
30778
30780
|
{
|
|
30779
30781
|
reason: "proximity",
|
|
30780
|
-
value: WEIGHT_PROXIMITY * clamp01$
|
|
30782
|
+
value: WEIGHT_PROXIMITY * clamp01$2(input.peakBboxAreaFrac / SIZE_FULL)
|
|
30781
30783
|
},
|
|
30782
30784
|
{
|
|
30783
30785
|
reason: "travel",
|
|
30784
|
-
value: WEIGHT_TRAVEL * clamp01$
|
|
30786
|
+
value: WEIGHT_TRAVEL * clamp01$2(input.totalDistance / 2)
|
|
30785
30787
|
},
|
|
30786
30788
|
{
|
|
30787
30789
|
reason: "zone",
|
|
@@ -30800,11 +30802,11 @@ function computeImportance(input) {
|
|
|
30800
30802
|
}
|
|
30801
30803
|
const identified = input.identityLabel !== void 0 && input.identityLabel.length > 0;
|
|
30802
30804
|
if (input.netDisplacementFrac !== void 0 && input.pathSpanFrac !== void 0 && input.netDisplacementFrac < .02 && input.pathSpanFrac < .04 && !identified) return {
|
|
30803
|
-
importance: clamp01$
|
|
30805
|
+
importance: clamp01$2(sum * STATIC_IMPORTANCE_MULTIPLIER),
|
|
30804
30806
|
reason: best.reason
|
|
30805
30807
|
};
|
|
30806
30808
|
return {
|
|
30807
|
-
importance: clamp01$
|
|
30809
|
+
importance: clamp01$2(sum),
|
|
30808
30810
|
reason: best.reason
|
|
30809
30811
|
};
|
|
30810
30812
|
}
|
|
@@ -33504,7 +33506,7 @@ function isEdgeAwareNewBest(current, candidate, hysteresis) {
|
|
|
33504
33506
|
* it, so it can be loose enough to see the near misses.
|
|
33505
33507
|
*/
|
|
33506
33508
|
var CO_MOVING_MIN_CONTAINMENT = .6;
|
|
33507
|
-
function intersectionArea$
|
|
33509
|
+
function intersectionArea$4(a, b) {
|
|
33508
33510
|
return Math.max(0, Math.min(a.x + a.w, b.x + b.w) - Math.max(a.x, b.x)) * Math.max(0, Math.min(a.y + a.h, b.y + b.h) - Math.max(a.y, b.y));
|
|
33509
33511
|
}
|
|
33510
33512
|
/**
|
|
@@ -33524,7 +33526,7 @@ function findCoMovingPairs(born, live, minContainment = CO_MOVING_MIN_CONTAINMEN
|
|
|
33524
33526
|
if (o.className === b.className) continue;
|
|
33525
33527
|
const areaO = o.bbox.w * o.bbox.h;
|
|
33526
33528
|
if (areaO <= 0) continue;
|
|
33527
|
-
const inter = intersectionArea$
|
|
33529
|
+
const inter = intersectionArea$4(b.bbox, o.bbox);
|
|
33528
33530
|
if (inter <= 0) continue;
|
|
33529
33531
|
const union = areaB + areaO - inter;
|
|
33530
33532
|
const containment = inter / Math.min(areaB, areaO);
|
|
@@ -33956,7 +33958,7 @@ function pairRiders(detections) {
|
|
|
33956
33958
|
* diagonal = 2.085) with the same ~10% headroom the old 1.35 gave it.
|
|
33957
33959
|
*/
|
|
33958
33960
|
var GROWTH_CENTROID_FACTOR = 2.3;
|
|
33959
|
-
function intersectionArea$
|
|
33961
|
+
function intersectionArea$3(a, b) {
|
|
33960
33962
|
const x1 = Math.max(a.x, b.x);
|
|
33961
33963
|
const y1 = Math.max(a.y, b.y);
|
|
33962
33964
|
const x2 = Math.min(a.x + a.w, b.x + b.w);
|
|
@@ -33994,7 +33996,7 @@ function growthAdjacentMatch(track, det, ctx) {
|
|
|
33994
33996
|
const larger = Math.max(trackArea, detArea);
|
|
33995
33997
|
const smaller = Math.min(trackArea, detArea);
|
|
33996
33998
|
if (larger / smaller < 1.5) return false;
|
|
33997
|
-
if (intersectionArea$
|
|
33999
|
+
if (intersectionArea$3(track, det) / smaller >= .5) return false;
|
|
33998
34000
|
const tc = bboxCentroid(track);
|
|
33999
34001
|
const dc = bboxCentroid(det);
|
|
34000
34002
|
return Math.hypot(tc.x - dc.x, tc.y - dc.y) <= GROWTH_CENTROID_FACTOR * Math.hypot(track.w, track.h);
|
|
@@ -46804,6 +46806,15 @@ var CACHE_CONTROL = "public, max-age=31536000, immutable";
|
|
|
46804
46806
|
var THUMB_DEFAULT_SIZE = 160;
|
|
46805
46807
|
var THUMB_MIN_SIZE = 64;
|
|
46806
46808
|
var THUMB_MAX_SIZE = 320;
|
|
46809
|
+
/** Set by the HUB for a `csv_*` share token admitted on this plane; never by a client. */
|
|
46810
|
+
var SHARE_DEVICES_HEADER = "x-camstack-share-devices";
|
|
46811
|
+
/** The device ids a share scope allows, or `null` when the request carries no scope. */
|
|
46812
|
+
function shareScopeDevices(headers) {
|
|
46813
|
+
const raw = headers[SHARE_DEVICES_HEADER];
|
|
46814
|
+
const value = Array.isArray(raw) ? raw.join(",") : raw;
|
|
46815
|
+
if (value === void 0) return null;
|
|
46816
|
+
return new Set(value.split(",").map((part) => Number.parseInt(part.trim(), 10)).filter((id) => Number.isInteger(id) && id >= 0));
|
|
46817
|
+
}
|
|
46807
46818
|
/**
|
|
46808
46819
|
* Parse the `?kind=…` query into a preferred stored media kind. Returns null
|
|
46809
46820
|
* when unset. The value is a free-form kind token (e.g. `crop`); the resolver
|
|
@@ -46876,6 +46887,14 @@ function createEventMediaHandler(deps) {
|
|
|
46876
46887
|
res.writeHead(404, { "server-timing": `resolve;dur=${(performance.now() - resolveStartedAt).toFixed(1)}` }).end();
|
|
46877
46888
|
return;
|
|
46878
46889
|
}
|
|
46890
|
+
const scope = shareScopeDevices(req.headers);
|
|
46891
|
+
if (scope !== null) {
|
|
46892
|
+
const owner = media.deviceId ?? null;
|
|
46893
|
+
if (owner === null || !scope.has(owner)) {
|
|
46894
|
+
res.writeHead(403).end();
|
|
46895
|
+
return;
|
|
46896
|
+
}
|
|
46897
|
+
}
|
|
46879
46898
|
const etag = "\"" + media.key + "\"";
|
|
46880
46899
|
const serverTiming = `resolve;dur=${(performance.now() - resolveStartedAt).toFixed(1)}`;
|
|
46881
46900
|
if (req.headers["if-none-match"] === etag) {
|
|
@@ -48346,7 +48365,7 @@ var RetrainAnnotationStore = class {
|
|
|
48346
48365
|
};
|
|
48347
48366
|
//#endregion
|
|
48348
48367
|
//#region src/pipeline-analytics/retrain/retrain-geometry.ts
|
|
48349
|
-
function clamp01(v) {
|
|
48368
|
+
function clamp01$1(v) {
|
|
48350
48369
|
return v < 0 ? 0 : v > 1 ? 1 : v;
|
|
48351
48370
|
}
|
|
48352
48371
|
/** Is this a usable rectangle at all? A zero-area box is not a subject. */
|
|
@@ -48422,10 +48441,10 @@ function deriveZoneCropRect(bbox, zone) {
|
|
|
48422
48441
|
function deriveSubjectCropRect(bbox, padding) {
|
|
48423
48442
|
if (!isUsableRect(bbox)) return null;
|
|
48424
48443
|
const pad = Number.isFinite(padding) && padding > 0 ? padding : 0;
|
|
48425
|
-
const cropX = clamp01(bbox.x - bbox.w * pad);
|
|
48426
|
-
const cropY = clamp01(bbox.y - bbox.h * pad);
|
|
48427
|
-
const cropRight = clamp01(bbox.x + bbox.w * (1 + pad));
|
|
48428
|
-
const cropBottom = clamp01(bbox.y + bbox.h * (1 + pad));
|
|
48444
|
+
const cropX = clamp01$1(bbox.x - bbox.w * pad);
|
|
48445
|
+
const cropY = clamp01$1(bbox.y - bbox.h * pad);
|
|
48446
|
+
const cropRight = clamp01$1(bbox.x + bbox.w * (1 + pad));
|
|
48447
|
+
const cropBottom = clamp01$1(bbox.y + bbox.h * (1 + pad));
|
|
48429
48448
|
const cropW = cropRight - cropX;
|
|
48430
48449
|
const cropH = cropBottom - cropY;
|
|
48431
48450
|
if (!(cropW > 0) || !(cropH > 0)) return null;
|
|
@@ -52676,7 +52695,7 @@ function isSceneSizedBox(bbox, frameWidth, frameHeight) {
|
|
|
52676
52695
|
const frame = frameWidth * frameHeight;
|
|
52677
52696
|
return frame > 0 && bbox.w * bbox.h / frame >= .55;
|
|
52678
52697
|
}
|
|
52679
|
-
function intersectionArea$
|
|
52698
|
+
function intersectionArea$2(a, b) {
|
|
52680
52699
|
const ix1 = Math.max(a.x, b.x);
|
|
52681
52700
|
const iy1 = Math.max(a.y, b.y);
|
|
52682
52701
|
const ix2 = Math.min(a.x + a.w, b.x + b.w);
|
|
@@ -52689,7 +52708,7 @@ function detectionSwallowsEntry(det, parked) {
|
|
|
52689
52708
|
const detArea = det.w * det.h;
|
|
52690
52709
|
if (entryArea <= 0 || detArea < entryArea * 1.35) return false;
|
|
52691
52710
|
if (!centroidInside(det, centroid(parked))) return false;
|
|
52692
|
-
return intersectionArea$
|
|
52711
|
+
return intersectionArea$2(parked, det) / entryArea >= SWALLOW_MIN_COVERAGE;
|
|
52693
52712
|
}
|
|
52694
52713
|
function parkedEntryHoldsDetection(entry, detBbox, config) {
|
|
52695
52714
|
const overlap = iou(entry.bbox, detBbox);
|
|
@@ -53521,7 +53540,7 @@ var VEHICLE_CLASSES = [
|
|
|
53521
53540
|
"vehicle"
|
|
53522
53541
|
];
|
|
53523
53542
|
/** Area of the intersection of two boxes. `0` when they do not overlap. */
|
|
53524
|
-
function intersectionArea(a, b) {
|
|
53543
|
+
function intersectionArea$1(a, b) {
|
|
53525
53544
|
const left = Math.max(a.x, b.x);
|
|
53526
53545
|
const right = Math.min(a.x + a.w, b.x + b.w);
|
|
53527
53546
|
const top = Math.max(a.y, b.y);
|
|
@@ -53538,7 +53557,7 @@ function intersectionArea(a, b) {
|
|
|
53538
53557
|
function containedFraction(person, vehicle) {
|
|
53539
53558
|
const area = person.w * person.h;
|
|
53540
53559
|
if (area <= 0) return 0;
|
|
53541
|
-
return intersectionArea(person, vehicle) / area;
|
|
53560
|
+
return intersectionArea$1(person, vehicle) / area;
|
|
53542
53561
|
}
|
|
53543
53562
|
/**
|
|
53544
53563
|
* Is this person box contained in any vehicle box in the same frame?
|
|
@@ -56437,9 +56456,11 @@ function isUniqueViolation(err) {
|
|
|
56437
56456
|
var SummaryStore = class {
|
|
56438
56457
|
store;
|
|
56439
56458
|
logger;
|
|
56459
|
+
onEvict;
|
|
56440
56460
|
constructor(deps) {
|
|
56441
56461
|
this.store = deps.store;
|
|
56442
56462
|
this.logger = deps.logger;
|
|
56463
|
+
this.onEvict = deps.onEvict;
|
|
56443
56464
|
}
|
|
56444
56465
|
static async declare(store) {
|
|
56445
56466
|
await Promise.all([store.declareCollection.mutate({
|
|
@@ -56637,6 +56658,18 @@ var SummaryStore = class {
|
|
|
56637
56658
|
bytes: 0
|
|
56638
56659
|
}];
|
|
56639
56660
|
}
|
|
56661
|
+
/** The subset of `ids` that still have a row — the orphan audit's probe. Throws on a failed read. */
|
|
56662
|
+
async existingIds(ids) {
|
|
56663
|
+
const found = /* @__PURE__ */ new Set();
|
|
56664
|
+
await Promise.all(ids.map(async (id) => {
|
|
56665
|
+
const raw = await this.store.get.query({
|
|
56666
|
+
collection: SUMMARIES_COLLECTION,
|
|
56667
|
+
key: id
|
|
56668
|
+
});
|
|
56669
|
+
if (typeof raw === "object" && raw !== null) found.add(id);
|
|
56670
|
+
}));
|
|
56671
|
+
return found;
|
|
56672
|
+
}
|
|
56640
56673
|
/** Age sweep on the device window: everything opened before `cutoffMs`, members included. */
|
|
56641
56674
|
async evictBefore(deviceId, cutoffMs) {
|
|
56642
56675
|
const rows = await this.store.query.query({
|
|
@@ -56649,6 +56682,7 @@ var SummaryStore = class {
|
|
|
56649
56682
|
});
|
|
56650
56683
|
if (rows.length === 0) return 0;
|
|
56651
56684
|
const ids = rows.map((r) => r.id);
|
|
56685
|
+
await this.onEvict?.(ids);
|
|
56652
56686
|
await this.store.deleteWhere.mutate({
|
|
56653
56687
|
collection: SUMMARY_MEMBERS_COLLECTION,
|
|
56654
56688
|
filter: { whereIn: { summaryId: ids } }
|
|
@@ -59171,6 +59205,10 @@ function resolveMediaOwner(row) {
|
|
|
59171
59205
|
kind: "event",
|
|
59172
59206
|
eventId: ownerId
|
|
59173
59207
|
};
|
|
59208
|
+
case "summary": return {
|
|
59209
|
+
kind: "summary",
|
|
59210
|
+
summaryId: ownerId
|
|
59211
|
+
};
|
|
59174
59212
|
case "face": return ownerId.startsWith("face-") ? {
|
|
59175
59213
|
kind: "track",
|
|
59176
59214
|
trackId: ownerId.slice(FACE_MEDIA_OWNER_PREFIX.length)
|
|
@@ -59257,11 +59295,14 @@ var DEFAULT_ORPHAN_LIMITS = {
|
|
|
59257
59295
|
function questionsFor(refs) {
|
|
59258
59296
|
const trackIds = /* @__PURE__ */ new Set();
|
|
59259
59297
|
const eventIds = /* @__PURE__ */ new Set();
|
|
59298
|
+
const summaryIds = /* @__PURE__ */ new Set();
|
|
59260
59299
|
for (const ref of refs) if (ref.kind === "track") trackIds.add(ref.trackId);
|
|
59261
59300
|
else if (ref.kind === "event") eventIds.add(ref.eventId);
|
|
59301
|
+
else if (ref.kind === "summary") summaryIds.add(ref.summaryId);
|
|
59262
59302
|
return {
|
|
59263
59303
|
trackIds: [...trackIds],
|
|
59264
|
-
eventIds: [...eventIds]
|
|
59304
|
+
eventIds: [...eventIds],
|
|
59305
|
+
summaryIds: [...summaryIds]
|
|
59265
59306
|
};
|
|
59266
59307
|
}
|
|
59267
59308
|
/**
|
|
@@ -59293,12 +59334,14 @@ async function auditScope(scope, deps, limits, cursor) {
|
|
|
59293
59334
|
break;
|
|
59294
59335
|
}
|
|
59295
59336
|
const refs = rows.map((row) => scope.resolve(row));
|
|
59296
|
-
const { trackIds, eventIds } = questionsFor(refs);
|
|
59337
|
+
const { trackIds, eventIds, summaryIds } = questionsFor(refs);
|
|
59297
59338
|
let liveTracks;
|
|
59298
59339
|
let liveEvents;
|
|
59340
|
+
let liveSummaries;
|
|
59299
59341
|
try {
|
|
59300
59342
|
liveTracks = trackIds.length > 0 ? await deps.liveTrackIds(trackIds) : /* @__PURE__ */ new Set();
|
|
59301
59343
|
liveEvents = eventIds.length > 0 ? await deps.liveEventIds(eventIds) : /* @__PURE__ */ new Set();
|
|
59344
|
+
liveSummaries = summaryIds.length > 0 ? await deps.liveSummaryIds(summaryIds) : /* @__PURE__ */ new Set();
|
|
59302
59345
|
} catch (err) {
|
|
59303
59346
|
deps.logger.warn("orphan audit: liveness probe failed — scope abandoned for this run", { meta: {
|
|
59304
59347
|
collection: scope.collection,
|
|
@@ -59322,7 +59365,7 @@ async function auditScope(scope, deps, limits, cursor) {
|
|
|
59322
59365
|
tally.unresolvable += 1;
|
|
59323
59366
|
continue;
|
|
59324
59367
|
}
|
|
59325
|
-
if (ref.kind === "track" ? liveTracks.has(ref.trackId) : ref.kind === "event" ? liveEvents.has(ref.eventId) : false) continue;
|
|
59368
|
+
if (ref.kind === "track" ? liveTracks.has(ref.trackId) : ref.kind === "event" ? liveEvents.has(ref.eventId) : ref.kind === "summary" ? liveSummaries.has(ref.summaryId) : false) continue;
|
|
59326
59369
|
const ts = num(row.data, scope.timestampField);
|
|
59327
59370
|
if (ts !== null && ts > graceFloor) {
|
|
59328
59371
|
tally.withinGrace += 1;
|
|
@@ -65274,6 +65317,196 @@ function rowFromMetadata(id, metadata) {
|
|
|
65274
65317
|
};
|
|
65275
65318
|
}
|
|
65276
65319
|
//#endregion
|
|
65320
|
+
//#region src/pipeline-analytics/summary/summary-score.ts
|
|
65321
|
+
/** A member whose own box is covered this much by others is hidden. */
|
|
65322
|
+
var OCCLUDED_COVER_FRACTION = .5;
|
|
65323
|
+
/** A box covering this share of the frame scores full marks on area. */
|
|
65324
|
+
var FULL_AREA_SHARE = .25;
|
|
65325
|
+
var WEIGHT_CONFIDENCE = .3;
|
|
65326
|
+
var WEIGHT_AREA = .25;
|
|
65327
|
+
var WEIGHT_RECOGNITION = .25;
|
|
65328
|
+
var WEIGHT_PLACEMENT = .2;
|
|
65329
|
+
function intersectionArea(a, b) {
|
|
65330
|
+
const w = Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x);
|
|
65331
|
+
const h = Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y);
|
|
65332
|
+
return w > 0 && h > 0 ? w * h : 0;
|
|
65333
|
+
}
|
|
65334
|
+
/**
|
|
65335
|
+
* Share of `own` covered by `others`, 0..1. Pairwise intersections summed and
|
|
65336
|
+
* capped at the own area: when two others overlap each other on top of this
|
|
65337
|
+
* box the sum over-counts, which only ever calls a member MORE hidden, never
|
|
65338
|
+
* less — the safe side for a shot meant to show everyone.
|
|
65339
|
+
*/
|
|
65340
|
+
function coveredFraction(own, others) {
|
|
65341
|
+
const area = own.width * own.height;
|
|
65342
|
+
if (area <= 0) return 1;
|
|
65343
|
+
const covered = others.reduce((sum, other) => sum + intersectionArea(own, other), 0);
|
|
65344
|
+
return Math.min(1, covered / area);
|
|
65345
|
+
}
|
|
65346
|
+
function clamp01(value) {
|
|
65347
|
+
return Math.max(0, Math.min(1, value));
|
|
65348
|
+
}
|
|
65349
|
+
function placement(box, frameWidth, frameHeight) {
|
|
65350
|
+
const cx = box.x + box.width / 2;
|
|
65351
|
+
const cy = box.y + box.height / 2;
|
|
65352
|
+
const dx = cx - frameWidth / 2;
|
|
65353
|
+
const dy = cy - frameHeight / 2;
|
|
65354
|
+
const halfDiagonal = Math.hypot(frameWidth, frameHeight) / 2;
|
|
65355
|
+
return halfDiagonal <= 0 ? 0 : 1 - clamp01(Math.hypot(dx, dy) / halfDiagonal);
|
|
65356
|
+
}
|
|
65357
|
+
function memberQuality(d, frameWidth, frameHeight) {
|
|
65358
|
+
const frameArea = frameWidth * frameHeight;
|
|
65359
|
+
const cappedArea = clamp01((frameArea <= 0 ? 0 : d.bbox.width * d.bbox.height / frameArea) / FULL_AREA_SHARE);
|
|
65360
|
+
return WEIGHT_CONFIDENCE * clamp01(d.confidence) + WEIGHT_AREA * cappedArea + WEIGHT_RECOGNITION * (d.recognized ? 1 : 0) + WEIGHT_PLACEMENT * placement(d.bbox, frameWidth, frameHeight);
|
|
65361
|
+
}
|
|
65362
|
+
/** Score one frame for one summary. Pure; never throws on empty input. */
|
|
65363
|
+
function scoreSummaryFrame(input) {
|
|
65364
|
+
const present = input.detections.filter((d) => input.members.has(d.trackId));
|
|
65365
|
+
const visible = present.filter((d) => coveredFraction(d.bbox, present.filter((o) => o !== d).map((o) => o.bbox)) < OCCLUDED_COVER_FRACTION);
|
|
65366
|
+
if (visible.length === 0) return {
|
|
65367
|
+
visible: 0,
|
|
65368
|
+
quality: 0
|
|
65369
|
+
};
|
|
65370
|
+
const quality = visible.reduce((sum, d) => sum + memberQuality(d, input.frameWidth, input.frameHeight), 0) / visible.length;
|
|
65371
|
+
return {
|
|
65372
|
+
visible: visible.length,
|
|
65373
|
+
quality
|
|
65374
|
+
};
|
|
65375
|
+
}
|
|
65376
|
+
/**
|
|
65377
|
+
* Lexicographic: more visible members wins outright; only equal counts fall
|
|
65378
|
+
* through to quality. Positive when `a` is the better shot.
|
|
65379
|
+
*/
|
|
65380
|
+
function compareSummaryScores(a, b) {
|
|
65381
|
+
if (a.visible !== b.visible) return a.visible - b.visible;
|
|
65382
|
+
return a.quality - b.quality;
|
|
65383
|
+
}
|
|
65384
|
+
/** A candidate replaces the best only when it is STRICTLY better — a tie keeps
|
|
65385
|
+
* the pixels already materialised (they cost a native round trip). */
|
|
65386
|
+
function isBetterSummaryScore(candidate, best) {
|
|
65387
|
+
if (candidate.visible === 0) return false;
|
|
65388
|
+
return best === null || compareSummaryScores(candidate, best) > 0;
|
|
65389
|
+
}
|
|
65390
|
+
//#endregion
|
|
65391
|
+
//#region src/pipeline-analytics/summary/group-shot.ts
|
|
65392
|
+
/**
|
|
65393
|
+
* The running argmax of one open summary's group shot.
|
|
65394
|
+
*
|
|
65395
|
+
* RAM only, on purpose: it decides WHEN pixels are worth a native round trip
|
|
65396
|
+
* (only when the maximum improves — D9/D18), it is not the record of the
|
|
65397
|
+
* shot. The record is the media row the caller writes under the summary's
|
|
65398
|
+
* id; after a restart an open summary simply starts its argmax again and
|
|
65399
|
+
* replaces the row the next time it beats what it can remember, which is
|
|
65400
|
+
* nothing. That is a weaker shot for one session, never a wrong one.
|
|
65401
|
+
*/
|
|
65402
|
+
var GroupShotTracker = class {
|
|
65403
|
+
best = /* @__PURE__ */ new Map();
|
|
65404
|
+
/**
|
|
65405
|
+
* Offer a frame. Returns `true` when it beats the summary's best so far —
|
|
65406
|
+
* the caller's cue to materialise the pixels for THIS frame, now, while its
|
|
65407
|
+
* handle is still leased.
|
|
65408
|
+
*/
|
|
65409
|
+
offer(summaryId, score, frame) {
|
|
65410
|
+
if (!isBetterSummaryScore(score, (this.best.get(summaryId) ?? null)?.score ?? null)) return false;
|
|
65411
|
+
this.best.set(summaryId, {
|
|
65412
|
+
score,
|
|
65413
|
+
frame
|
|
65414
|
+
});
|
|
65415
|
+
return true;
|
|
65416
|
+
}
|
|
65417
|
+
bestOf(summaryId) {
|
|
65418
|
+
return this.best.get(summaryId) ?? null;
|
|
65419
|
+
}
|
|
65420
|
+
/** A sealed or evicted summary takes no more frames; drop its state. */
|
|
65421
|
+
forget(summaryId) {
|
|
65422
|
+
this.best.delete(summaryId);
|
|
65423
|
+
}
|
|
65424
|
+
get size() {
|
|
65425
|
+
return this.best.size;
|
|
65426
|
+
}
|
|
65427
|
+
};
|
|
65428
|
+
//#endregion
|
|
65429
|
+
//#region src/pipeline-analytics/summary/group-shot-materialiser.ts
|
|
65430
|
+
async function persistSummaryGroupShot(deps, input) {
|
|
65431
|
+
const { deviceId, summaryId, frameHandle } = input;
|
|
65432
|
+
const drop = {
|
|
65433
|
+
tags: { deviceId },
|
|
65434
|
+
meta: {
|
|
65435
|
+
summaryId,
|
|
65436
|
+
nodeId: frameHandle.nodeId
|
|
65437
|
+
}
|
|
65438
|
+
};
|
|
65439
|
+
try {
|
|
65440
|
+
const result = await deps.fetchNativeFullFrame(frameHandle, input.maxWidth);
|
|
65441
|
+
const native = result?.frame;
|
|
65442
|
+
if (!native || native.format !== "rgb" || native.width <= 0 || native.height <= 0) {
|
|
65443
|
+
deps.logger.warn("group shot native miss — the previous shot stands", {
|
|
65444
|
+
...drop,
|
|
65445
|
+
meta: {
|
|
65446
|
+
...drop.meta,
|
|
65447
|
+
shmId: frameHandle.shmId
|
|
65448
|
+
}
|
|
65449
|
+
});
|
|
65450
|
+
return { kind: "native-miss" };
|
|
65451
|
+
}
|
|
65452
|
+
if (!keyFrameAcceptsTier(result.tier)) {
|
|
65453
|
+
deps.logger.warn("group shot RAM tier rejected — the previous shot stands", {
|
|
65454
|
+
...drop,
|
|
65455
|
+
meta: {
|
|
65456
|
+
...drop.meta,
|
|
65457
|
+
shmId: frameHandle.shmId,
|
|
65458
|
+
tier: result.tier
|
|
65459
|
+
}
|
|
65460
|
+
});
|
|
65461
|
+
return { kind: "ram-tier" };
|
|
65462
|
+
}
|
|
65463
|
+
const { keyFrame, keyFrameSmall } = await deps.encodeVariants(native);
|
|
65464
|
+
const keyFrameKey = await deps.media.putReplacing({
|
|
65465
|
+
deviceId,
|
|
65466
|
+
ownerKind: "summary",
|
|
65467
|
+
ownerId: summaryId,
|
|
65468
|
+
kind: "keyFrame",
|
|
65469
|
+
timestamp: input.timestamp,
|
|
65470
|
+
data: keyFrame
|
|
65471
|
+
});
|
|
65472
|
+
try {
|
|
65473
|
+
await deps.media.putReplacing({
|
|
65474
|
+
deviceId,
|
|
65475
|
+
ownerKind: "summary",
|
|
65476
|
+
ownerId: summaryId,
|
|
65477
|
+
kind: "keyFrameSmall",
|
|
65478
|
+
timestamp: input.timestamp,
|
|
65479
|
+
data: keyFrameSmall
|
|
65480
|
+
});
|
|
65481
|
+
} catch (err) {
|
|
65482
|
+
deps.logger.warn("group shot web companion write failed — the native shot is stored", {
|
|
65483
|
+
...drop,
|
|
65484
|
+
meta: {
|
|
65485
|
+
...drop.meta,
|
|
65486
|
+
error: require_dist.errMsg(err)
|
|
65487
|
+
}
|
|
65488
|
+
});
|
|
65489
|
+
}
|
|
65490
|
+
return {
|
|
65491
|
+
kind: "landed",
|
|
65492
|
+
keyFrameKey
|
|
65493
|
+
};
|
|
65494
|
+
} catch (err) {
|
|
65495
|
+
const error = require_dist.errMsg(err);
|
|
65496
|
+
deps.logger.warn("group shot capture failed", {
|
|
65497
|
+
...drop,
|
|
65498
|
+
meta: {
|
|
65499
|
+
...drop.meta,
|
|
65500
|
+
error
|
|
65501
|
+
}
|
|
65502
|
+
});
|
|
65503
|
+
return {
|
|
65504
|
+
kind: "error",
|
|
65505
|
+
error
|
|
65506
|
+
};
|
|
65507
|
+
}
|
|
65508
|
+
}
|
|
65509
|
+
//#endregion
|
|
65277
65510
|
//#region src/pipeline-analytics/summary/summary-sealer.ts
|
|
65278
65511
|
function errMsg(err) {
|
|
65279
65512
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -65281,16 +65514,33 @@ function errMsg(err) {
|
|
|
65281
65514
|
var SummarySealer = class {
|
|
65282
65515
|
deps;
|
|
65283
65516
|
sweepInFlight = false;
|
|
65517
|
+
/**
|
|
65518
|
+
* What the FRAME path may ask at frame-rate: the open summary of a camera
|
|
65519
|
+
* and the members it has seen THIS process open. Fed only by this class's
|
|
65520
|
+
* own writes, never the authority (D224): a fresh process answers `null`
|
|
65521
|
+
* until a track opens, and a summary open across a restart is scored on the
|
|
65522
|
+
* members that open after it — a weaker shot, never a wrong one.
|
|
65523
|
+
*/
|
|
65524
|
+
openMirror = /* @__PURE__ */ new Map();
|
|
65284
65525
|
constructor(deps) {
|
|
65285
65526
|
this.deps = deps;
|
|
65286
65527
|
}
|
|
65528
|
+
openFor(deviceId) {
|
|
65529
|
+
return this.openMirror.get(deviceId) ?? null;
|
|
65530
|
+
}
|
|
65287
65531
|
async onTrackOpened(input) {
|
|
65288
65532
|
try {
|
|
65289
|
-
await this.deps.summaries.openOrAbsorb({
|
|
65533
|
+
const row = await this.deps.summaries.openOrAbsorb({
|
|
65290
65534
|
deviceId: input.deviceId,
|
|
65291
65535
|
trackId: input.trackId,
|
|
65292
65536
|
at: this.deps.now()
|
|
65293
65537
|
});
|
|
65538
|
+
const mirror = this.openMirror.get(input.deviceId);
|
|
65539
|
+
if (mirror !== void 0 && mirror.id === row.id) mirror.members.add(input.trackId);
|
|
65540
|
+
else this.openMirror.set(input.deviceId, {
|
|
65541
|
+
id: row.id,
|
|
65542
|
+
members: new Set([input.trackId])
|
|
65543
|
+
});
|
|
65294
65544
|
} catch (err) {
|
|
65295
65545
|
this.deps.logger.warn("summary: could not open or absorb for a track", {
|
|
65296
65546
|
tags: { deviceId: input.deviceId },
|
|
@@ -65346,13 +65596,19 @@ var SummarySealer = class {
|
|
|
65346
65596
|
if (!quiet) return;
|
|
65347
65597
|
const sealedAt = this.deps.now();
|
|
65348
65598
|
try {
|
|
65349
|
-
if (await this.deps.summaries.seal(summary, sealedAt))
|
|
65350
|
-
|
|
65351
|
-
|
|
65352
|
-
|
|
65353
|
-
|
|
65354
|
-
|
|
65355
|
-
|
|
65599
|
+
if (await this.deps.summaries.seal(summary, sealedAt)) {
|
|
65600
|
+
const mirror = this.openMirror.get(deviceId);
|
|
65601
|
+
const members = mirror?.id === summaryId ? mirror.members : /* @__PURE__ */ new Set();
|
|
65602
|
+
if (mirror?.id === summaryId) this.openMirror.delete(deviceId);
|
|
65603
|
+
this.deps.onSealed?.(summaryId, members);
|
|
65604
|
+
this.deps.logger.info("summary sealed", {
|
|
65605
|
+
tags: { deviceId },
|
|
65606
|
+
meta: {
|
|
65607
|
+
summaryId,
|
|
65608
|
+
sealedAt
|
|
65609
|
+
}
|
|
65610
|
+
});
|
|
65611
|
+
}
|
|
65356
65612
|
} catch (err) {
|
|
65357
65613
|
this.deps.logger.warn("summary sweep: seal failed — the row stays open for the next sweep", {
|
|
65358
65614
|
tags: { deviceId },
|
|
@@ -66681,6 +66937,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
66681
66937
|
summaryStore = null;
|
|
66682
66938
|
/** The ONLY writer of a summary's lifecycle (D359). */
|
|
66683
66939
|
summarySealer = null;
|
|
66940
|
+
/** The running argmax of every open summary's group shot (D359 phase 3). RAM only. */
|
|
66941
|
+
groupShots = new GroupShotTracker();
|
|
66942
|
+
/**
|
|
66943
|
+
* Tracks a gallery match has named, for the group-shot score's recognition
|
|
66944
|
+
* term. "Recognised so far" rather than "at that instant": the match arrives
|
|
66945
|
+
* on the detail plane, after the frame; the frames that follow carry it.
|
|
66946
|
+
* Pruned when the track's summary seals, so it is bounded by open summaries.
|
|
66947
|
+
*/
|
|
66948
|
+
recognizedTrackIds = /* @__PURE__ */ new Set();
|
|
66684
66949
|
/** Ingest-side reverse index (sensor device → linked camera ids), TTL-cached
|
|
66685
66950
|
* so the DeviceStateChanged handler stays cheap. */
|
|
66686
66951
|
linkedCamerasCache = null;
|
|
@@ -68183,14 +68448,21 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
68183
68448
|
});
|
|
68184
68449
|
this.summaryStore = new SummaryStore({
|
|
68185
68450
|
store: api.settingsStore,
|
|
68186
|
-
logger: this.ctx.logger.child("summary")
|
|
68451
|
+
logger: this.ctx.logger.child("summary"),
|
|
68452
|
+
onEvict: async (ids) => {
|
|
68453
|
+
await this.mediaStore?.deleteForOwner("summary", ids);
|
|
68454
|
+
}
|
|
68187
68455
|
});
|
|
68188
68456
|
this.summarySealer = new SummarySealer({
|
|
68189
68457
|
summaries: this.summaryStore,
|
|
68190
68458
|
openTrackIds: async (deviceId) => this.residents.trackIdsForDevice(deviceId),
|
|
68191
68459
|
isCameraQuiet: async (deviceId) => (this.stationaryRegistry?.count(deviceId) ?? 0) === 0,
|
|
68192
68460
|
now: () => Date.now(),
|
|
68193
|
-
logger: this.ctx.logger.child("summary-sealer")
|
|
68461
|
+
logger: this.ctx.logger.child("summary-sealer"),
|
|
68462
|
+
onSealed: (summaryId, members) => {
|
|
68463
|
+
this.groupShots.forget(summaryId);
|
|
68464
|
+
for (const trackId of members) this.recognizedTrackIds.delete(trackId);
|
|
68465
|
+
}
|
|
68194
68466
|
});
|
|
68195
68467
|
this.packageDropDetector = new PackageDropDetector({
|
|
68196
68468
|
events: eventStore,
|
|
@@ -69039,6 +69311,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
69039
69311
|
this.eventMediaDataPlane = await this.ctx.dataPlane?.serve({
|
|
69040
69312
|
prefix: "event-media",
|
|
69041
69313
|
access: planeAccess("event-media"),
|
|
69314
|
+
share: ["events-view"],
|
|
69042
69315
|
handler
|
|
69043
69316
|
}) ?? null;
|
|
69044
69317
|
this.eventMediaBaseUrl = this.eventMediaDataPlane !== null ? `/addon/${this.ctx.id}/event-media` : null;
|
|
@@ -70625,6 +70898,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
70625
70898
|
for (const crops of childCropsByEvent.values()) for (const c of crops) if (c.kind === "faceCrop") faceCrops += 1;
|
|
70626
70899
|
else plateCrops += 1;
|
|
70627
70900
|
const snapshotTargets = this.buildSnapshotTargets(deviceId, result.tracked, result.timestamp, mediaSettings, result.frameWidth, result.frameHeight);
|
|
70901
|
+
this.offerGroupShot(deviceId, result, frameHandle);
|
|
70628
70902
|
for (const retry of this.collectFirstFrameRetries(deviceId, result.tracked, result.timestamp, firstFrameTargets)) firstFrameTargets.push(retry);
|
|
70629
70903
|
const targetedThisFrame = new Set([...firstFrameTargets.map((t) => t.trackId), ...snapshotTargets.map((s) => s.trackId)]);
|
|
70630
70904
|
const rasterFallbackCandidates = [];
|
|
@@ -71507,6 +71781,63 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
71507
71781
|
* is the scheduling (coalescing, priority, budget) and the bookkeeping the
|
|
71508
71782
|
* retry gate reads.
|
|
71509
71783
|
*/
|
|
71784
|
+
/**
|
|
71785
|
+
* Offer one processed frame to the camera's OPEN summary (D359 phase 3).
|
|
71786
|
+
* Metadata only, at frame-rate: the frame is scored against the members the
|
|
71787
|
+
* sealer has mirrored, and the pixels are fetched — by handle, at the owning
|
|
71788
|
+
* node, as a native key frame — only when this frame beats the best so far.
|
|
71789
|
+
*/
|
|
71790
|
+
offerGroupShot(deviceId, result, frameHandle) {
|
|
71791
|
+
const open = this.summarySealer?.openFor(deviceId);
|
|
71792
|
+
if (!open || frameHandle === void 0) return;
|
|
71793
|
+
const score = scoreSummaryFrame({
|
|
71794
|
+
members: open.members,
|
|
71795
|
+
frameWidth: result.frameWidth,
|
|
71796
|
+
frameHeight: result.frameHeight,
|
|
71797
|
+
detections: result.tracked.map((t) => ({
|
|
71798
|
+
trackId: t.trackId,
|
|
71799
|
+
bbox: {
|
|
71800
|
+
x: t.bbox.x,
|
|
71801
|
+
y: t.bbox.y,
|
|
71802
|
+
width: t.bbox.w,
|
|
71803
|
+
height: t.bbox.h
|
|
71804
|
+
},
|
|
71805
|
+
confidence: t.confidence,
|
|
71806
|
+
recognized: this.recognizedTrackIds.has(t.trackId)
|
|
71807
|
+
}))
|
|
71808
|
+
});
|
|
71809
|
+
if (!this.groupShots.offer(open.id, score, { timestamp: result.timestamp })) return;
|
|
71810
|
+
const getNative = this.getNativeKeyFrameRgb;
|
|
71811
|
+
const mediaStore = this.mediaStore;
|
|
71812
|
+
if (!getNative || !mediaStore) return;
|
|
71813
|
+
const summaryId = open.id;
|
|
71814
|
+
this.captureScheduler.request({
|
|
71815
|
+
deviceId,
|
|
71816
|
+
kind: "keyFrame",
|
|
71817
|
+
coalesceKey: `summary:${summaryId}:keyFrame`,
|
|
71818
|
+
exec: () => persistSummaryGroupShot({
|
|
71819
|
+
fetchNativeFullFrame: getNative,
|
|
71820
|
+
encodeVariants: encodeKeyFrameVariants,
|
|
71821
|
+
media: mediaStore,
|
|
71822
|
+
logger: this.ctx.logger
|
|
71823
|
+
}, {
|
|
71824
|
+
deviceId,
|
|
71825
|
+
summaryId,
|
|
71826
|
+
timestamp: result.timestamp,
|
|
71827
|
+
frameHandle,
|
|
71828
|
+
maxWidth: KEYFRAME_NATIVE_MAX_WIDTH
|
|
71829
|
+
})
|
|
71830
|
+
}).then((outcome) => {
|
|
71831
|
+
if (outcome?.kind === "landed") this.ctx.logger.debug("summary group shot landed", {
|
|
71832
|
+
tags: { deviceId },
|
|
71833
|
+
meta: {
|
|
71834
|
+
summaryId,
|
|
71835
|
+
visible: score.visible,
|
|
71836
|
+
quality: Number(score.quality.toFixed(3))
|
|
71837
|
+
}
|
|
71838
|
+
});
|
|
71839
|
+
}).catch(() => {});
|
|
71840
|
+
}
|
|
71510
71841
|
async persistKeyFrames(deviceId, timestamp, captures, frameHandle) {
|
|
71511
71842
|
const getNative = this.getNativeKeyFrameRgb;
|
|
71512
71843
|
const mediaStore = this.mediaStore;
|
|
@@ -71651,6 +71982,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
71651
71982
|
* FaceRecognizer's per-frame auto-match, and `routeDetailResults`' plate /
|
|
71652
71983
|
* fine-classification attribution. */
|
|
71653
71984
|
promoteSemanticRecognition(input) {
|
|
71985
|
+
this.recognizedTrackIds.add(input.trackId);
|
|
71654
71986
|
const ctx = this.ctxIfReady;
|
|
71655
71987
|
if (ctx === null) return;
|
|
71656
71988
|
promoteSemanticBestFrame({
|
|
@@ -73889,6 +74221,11 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
73889
74221
|
},
|
|
73890
74222
|
liveTrackIds: (ids) => trackStore.existingTrackIds(ids),
|
|
73891
74223
|
liveEventIds: (ids) => eventStore.existingEventIds(ids),
|
|
74224
|
+
liveSummaryIds: (ids) => {
|
|
74225
|
+
const summaries = this.summaryStore;
|
|
74226
|
+
if (!summaries) throw new Error("summary store not built — probe unavailable");
|
|
74227
|
+
return summaries.existingIds(ids);
|
|
74228
|
+
},
|
|
73892
74229
|
reclaim: async (collection, key) => {
|
|
73893
74230
|
if (collection === "pipeline-analytics:media") {
|
|
73894
74231
|
await mediaStore.deleteByKey(key);
|
|
@@ -75523,7 +75860,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
75523
75860
|
if (!file) return null;
|
|
75524
75861
|
return {
|
|
75525
75862
|
bytes: Buffer.from(file.base64, "base64"),
|
|
75526
|
-
key: file.key
|
|
75863
|
+
key: file.key,
|
|
75864
|
+
deviceId: file.deviceId
|
|
75527
75865
|
};
|
|
75528
75866
|
}
|
|
75529
75867
|
/**
|
|
@@ -75537,7 +75875,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
75537
75875
|
try {
|
|
75538
75876
|
return {
|
|
75539
75877
|
bytes: await makeSquareThumb(media.bytes, variant.size),
|
|
75540
|
-
key: `${media.key}|t${variant.size}
|
|
75878
|
+
key: `${media.key}|t${variant.size}`,
|
|
75879
|
+
deviceId: media.deviceId
|
|
75541
75880
|
};
|
|
75542
75881
|
} catch (err) {
|
|
75543
75882
|
this.ctx.logger.debug("event media: thumb variant failed — serving full", { meta: {
|
|
@@ -75567,7 +75906,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
75567
75906
|
const file = await (this.mediaStore?.getByKey(key) ?? Promise.resolve(null));
|
|
75568
75907
|
return file === null ? null : {
|
|
75569
75908
|
bytes: Buffer.from(file.base64, "base64"),
|
|
75570
|
-
key: file.key
|
|
75909
|
+
key: file.key,
|
|
75910
|
+
deviceId: file.deviceId
|
|
75571
75911
|
};
|
|
75572
75912
|
};
|
|
75573
75913
|
const eventFiles = await listInfo("event", id);
|