@camstack/addon-post-analysis 1.2.63 → 1.2.64
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-D4fGaA71.js → dist-BQ-M7jn9.js} +301 -36
- package/dist/{dist-CdqESw05.mjs → dist-CSTtfEOC.mjs} +301 -36
- 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-DrgptocJ.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-lSMqbbY-.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-DJwl-swi.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-EHTrRb5u.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-C8v4WVLy.mjs → hostInit-D5rpu5pY.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +273 -54
- package/dist/pipeline-analytics/index.mjs +273 -54
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +2 -2
|
@@ -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-BQ-M7jn9.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");
|
|
@@ -15784,6 +15784,7 @@ function resolveFaceSettings(raw) {
|
|
|
15784
15784
|
*/
|
|
15785
15785
|
function createLocationAwareMediaStorage(deps) {
|
|
15786
15786
|
const roots = /* @__PURE__ */ new Map();
|
|
15787
|
+
let defaultBypassInvalidated = false;
|
|
15787
15788
|
const rootOf = async (locationId) => {
|
|
15788
15789
|
const cached = roots.get(locationId);
|
|
15789
15790
|
if (cached !== void 0) return cached;
|
|
@@ -15794,21 +15795,55 @@ function createLocationAwareMediaStorage(deps) {
|
|
|
15794
15795
|
const absOf = async (locationId, relativePath) => node_path.default.join(await rootOf(locationId), relativePath);
|
|
15795
15796
|
return {
|
|
15796
15797
|
write: async (input) => {
|
|
15797
|
-
if (input.location === deps.defaultLocation) return deps.base.write(input);
|
|
15798
|
+
if (input.location === deps.defaultLocation && !defaultBypassInvalidated) return deps.base.write(input);
|
|
15798
15799
|
const abs = await absOf(input.location, input.relativePath);
|
|
15799
15800
|
await node_fs.promises.mkdir(node_path.default.dirname(abs), { recursive: true });
|
|
15800
15801
|
await node_fs.promises.writeFile(abs, input.data);
|
|
15801
15802
|
},
|
|
15802
15803
|
read: async (input) => {
|
|
15803
|
-
if (input.location === deps.defaultLocation) return deps.base.read(input);
|
|
15804
|
+
if (input.location === deps.defaultLocation && !defaultBypassInvalidated) return deps.base.read(input);
|
|
15804
15805
|
return node_fs.promises.readFile(await absOf(input.location, input.relativePath));
|
|
15805
15806
|
},
|
|
15806
15807
|
delete: async (input) => {
|
|
15807
|
-
if (input.location === deps.defaultLocation) return deps.base.delete(input);
|
|
15808
|
+
if (input.location === deps.defaultLocation && !defaultBypassInvalidated) return deps.base.delete(input);
|
|
15808
15809
|
await node_fs.promises.rm(await absOf(input.location, input.relativePath), { force: true });
|
|
15810
|
+
},
|
|
15811
|
+
invalidateRoots: () => {
|
|
15812
|
+
roots.clear();
|
|
15813
|
+
defaultBypassInvalidated = true;
|
|
15809
15814
|
}
|
|
15810
15815
|
};
|
|
15811
15816
|
}
|
|
15817
|
+
//#endregion
|
|
15818
|
+
//#region src/pipeline-analytics/media-write-gate.ts
|
|
15819
|
+
/**
|
|
15820
|
+
* Reversible writer gate for storage maintenance. `pause()` closes admission
|
|
15821
|
+
* first, then waits for writes already admitted to settle; callers never
|
|
15822
|
+
* mutate device bindings or analytics configuration to quiesce media.
|
|
15823
|
+
*/
|
|
15824
|
+
var MediaWriteGate = class {
|
|
15825
|
+
paused = false;
|
|
15826
|
+
active = 0;
|
|
15827
|
+
drainedWaiters = [];
|
|
15828
|
+
async run(write) {
|
|
15829
|
+
if (this.paused) throw new Error("event-media writes are paused for storage migration");
|
|
15830
|
+
this.active++;
|
|
15831
|
+
try {
|
|
15832
|
+
return await write();
|
|
15833
|
+
} finally {
|
|
15834
|
+
this.active--;
|
|
15835
|
+
if (this.active === 0) for (const resolve of this.drainedWaiters.splice(0)) resolve();
|
|
15836
|
+
}
|
|
15837
|
+
}
|
|
15838
|
+
pause() {
|
|
15839
|
+
this.paused = true;
|
|
15840
|
+
if (this.active === 0) return Promise.resolve();
|
|
15841
|
+
return new Promise((resolve) => this.drainedWaiters.push(resolve));
|
|
15842
|
+
}
|
|
15843
|
+
resume() {
|
|
15844
|
+
this.paused = false;
|
|
15845
|
+
}
|
|
15846
|
+
};
|
|
15812
15847
|
var MACRO_SET = new Set([
|
|
15813
15848
|
"person",
|
|
15814
15849
|
"vehicle",
|
|
@@ -16679,7 +16714,14 @@ var RETRAIN_FRAME_COLUMNS = [
|
|
|
16679
16714
|
name: "copiedAt",
|
|
16680
16715
|
type: "INTEGER",
|
|
16681
16716
|
notNull: true
|
|
16682
|
-
}
|
|
16717
|
+
},
|
|
16718
|
+
(
|
|
16719
|
+
/** NULL = current eventMedia default; stamped when migration moves a copy
|
|
16720
|
+
* before the logical default is repointed. */
|
|
16721
|
+
{
|
|
16722
|
+
name: "locationId",
|
|
16723
|
+
type: "TEXT"
|
|
16724
|
+
})
|
|
16683
16725
|
];
|
|
16684
16726
|
var RETRAIN_FRAME_INDEXES = [{
|
|
16685
16727
|
name: "idx_retrain_frames_track",
|
|
@@ -16708,12 +16750,14 @@ var RetrainFrameStore = class {
|
|
|
16708
16750
|
logger;
|
|
16709
16751
|
readSource;
|
|
16710
16752
|
now;
|
|
16753
|
+
writeGate;
|
|
16711
16754
|
constructor(deps) {
|
|
16712
16755
|
this.store = deps.store;
|
|
16713
16756
|
this.storage = deps.storage;
|
|
16714
16757
|
this.logger = deps.logger;
|
|
16715
16758
|
this.readSource = deps.readSource;
|
|
16716
16759
|
this.now = deps.now;
|
|
16760
|
+
this.writeGate = deps.writeGate;
|
|
16717
16761
|
}
|
|
16718
16762
|
static async declare(store) {
|
|
16719
16763
|
await store.declareCollection.mutate({
|
|
@@ -16732,7 +16776,8 @@ var RetrainFrameStore = class {
|
|
|
16732
16776
|
sizeBytes: Number(data["sizeBytes"] ?? 0),
|
|
16733
16777
|
width: Number(data["width"] ?? 0),
|
|
16734
16778
|
height: Number(data["height"] ?? 0),
|
|
16735
|
-
copiedAt: Number(data["copiedAt"] ?? 0)
|
|
16779
|
+
copiedAt: Number(data["copiedAt"] ?? 0),
|
|
16780
|
+
locationId: typeof data["locationId"] === "string" && data["locationId"].length > 0 ? data["locationId"] : null
|
|
16736
16781
|
};
|
|
16737
16782
|
}
|
|
16738
16783
|
/** The copy with this id, or `null`. Index read only — no blob is touched. */
|
|
@@ -16800,7 +16845,7 @@ var RetrainFrameStore = class {
|
|
|
16800
16845
|
async readFrame(frame) {
|
|
16801
16846
|
try {
|
|
16802
16847
|
return await this.storage.read({
|
|
16803
|
-
location:
|
|
16848
|
+
location: frame.locationId ?? "eventMedia",
|
|
16804
16849
|
relativePath: framePath(frame.deviceId, frame.trackId, frame.frameId)
|
|
16805
16850
|
});
|
|
16806
16851
|
} catch (err) {
|
|
@@ -16880,9 +16925,10 @@ var RetrainFrameStore = class {
|
|
|
16880
16925
|
sizeBytes: bytes.length,
|
|
16881
16926
|
width: size.width,
|
|
16882
16927
|
height: size.height,
|
|
16883
|
-
copiedAt: this.now()
|
|
16928
|
+
copiedAt: this.now(),
|
|
16929
|
+
locationId: null
|
|
16884
16930
|
};
|
|
16885
|
-
|
|
16931
|
+
const persist = async () => {
|
|
16886
16932
|
await this.storage.write({
|
|
16887
16933
|
location: RETRAIN_MEDIA_LOCATION,
|
|
16888
16934
|
relativePath: path,
|
|
@@ -16900,9 +16946,14 @@ var RetrainFrameStore = class {
|
|
|
16900
16946
|
sizeBytes: frame.sizeBytes,
|
|
16901
16947
|
width: frame.width,
|
|
16902
16948
|
height: frame.height,
|
|
16903
|
-
copiedAt: frame.copiedAt
|
|
16949
|
+
copiedAt: frame.copiedAt,
|
|
16950
|
+
locationId: null
|
|
16904
16951
|
}
|
|
16905
16952
|
});
|
|
16953
|
+
};
|
|
16954
|
+
try {
|
|
16955
|
+
if (this.writeGate) await this.writeGate.run(persist);
|
|
16956
|
+
else await persist();
|
|
16906
16957
|
} catch (err) {
|
|
16907
16958
|
this.logger.error("retrain copy failed to persist", {
|
|
16908
16959
|
tags: { deviceId: input.deviceId },
|
|
@@ -16943,26 +16994,30 @@ var RetrainFrameStore = class {
|
|
|
16943
16994
|
* best-effort write is how a half-deleted annotation set happens.
|
|
16944
16995
|
*/
|
|
16945
16996
|
async deleteFrame(frame) {
|
|
16946
|
-
|
|
16947
|
-
|
|
16948
|
-
|
|
16949
|
-
|
|
16950
|
-
|
|
16951
|
-
|
|
16952
|
-
|
|
16953
|
-
|
|
16954
|
-
|
|
16955
|
-
|
|
16956
|
-
|
|
16957
|
-
|
|
16958
|
-
|
|
16959
|
-
|
|
16960
|
-
|
|
16961
|
-
|
|
16962
|
-
|
|
16963
|
-
|
|
16964
|
-
|
|
16965
|
-
|
|
16997
|
+
const remove = async () => {
|
|
16998
|
+
try {
|
|
16999
|
+
await this.storage.delete({
|
|
17000
|
+
location: frame.locationId ?? "eventMedia",
|
|
17001
|
+
relativePath: framePath(frame.deviceId, frame.trackId, frame.frameId)
|
|
17002
|
+
});
|
|
17003
|
+
} catch {}
|
|
17004
|
+
try {
|
|
17005
|
+
await this.store.delete.mutate({
|
|
17006
|
+
collection: RETRAIN_FRAMES_COLLECTION,
|
|
17007
|
+
key: frame.frameId
|
|
17008
|
+
});
|
|
17009
|
+
} catch (err) {
|
|
17010
|
+
this.logger.warn("RetrainFrameStore.deleteFrame row delete failed", {
|
|
17011
|
+
tags: { deviceId: frame.deviceId },
|
|
17012
|
+
meta: {
|
|
17013
|
+
frameId: frame.frameId,
|
|
17014
|
+
error: String(err)
|
|
17015
|
+
}
|
|
17016
|
+
});
|
|
17017
|
+
}
|
|
17018
|
+
};
|
|
17019
|
+
if (this.writeGate) await this.writeGate.run(remove);
|
|
17020
|
+
else await remove();
|
|
16966
17021
|
}
|
|
16967
17022
|
};
|
|
16968
17023
|
//#endregion
|
|
@@ -18001,10 +18056,15 @@ var MediaStore = class {
|
|
|
18001
18056
|
storage;
|
|
18002
18057
|
store;
|
|
18003
18058
|
logger;
|
|
18059
|
+
writeGate;
|
|
18004
18060
|
constructor(deps) {
|
|
18005
18061
|
this.storage = deps.storage;
|
|
18006
18062
|
this.store = deps.store;
|
|
18007
18063
|
this.logger = deps.logger;
|
|
18064
|
+
this.writeGate = deps.writeGate;
|
|
18065
|
+
}
|
|
18066
|
+
gated(mutation) {
|
|
18067
|
+
return this.writeGate ? this.writeGate.run(mutation) : mutation();
|
|
18008
18068
|
}
|
|
18009
18069
|
static async declare(store) {
|
|
18010
18070
|
await store.declareCollection.mutate({
|
|
@@ -18016,6 +18076,10 @@ var MediaStore = class {
|
|
|
18016
18076
|
/** Persist a media blob and register its metadata row. Returns the
|
|
18017
18077
|
* collection key (MediaFile.key) that callers store as reference. */
|
|
18018
18078
|
async put(params) {
|
|
18079
|
+
if (this.writeGate) return this.writeGate.run(() => this.putUnchecked(params));
|
|
18080
|
+
return this.putUnchecked(params);
|
|
18081
|
+
}
|
|
18082
|
+
async putUnchecked(params) {
|
|
18019
18083
|
const key = buildKey(params);
|
|
18020
18084
|
const path = buildPath(params);
|
|
18021
18085
|
const record = {
|
|
@@ -18086,6 +18150,9 @@ var MediaStore = class {
|
|
|
18086
18150
|
* scheme (transition back-compat); once migrated it is a no-op.
|
|
18087
18151
|
*/
|
|
18088
18152
|
async putReplacing(params) {
|
|
18153
|
+
return this.gated(() => this.putReplacingUnchecked(params));
|
|
18154
|
+
}
|
|
18155
|
+
async putReplacingUnchecked(params) {
|
|
18089
18156
|
const existing = await this.store.query.query({
|
|
18090
18157
|
collection: MEDIA_COLLECTION,
|
|
18091
18158
|
filter: { where: {
|
|
@@ -18094,7 +18161,7 @@ var MediaStore = class {
|
|
|
18094
18161
|
kind: params.kind
|
|
18095
18162
|
} }
|
|
18096
18163
|
});
|
|
18097
|
-
const newKey = await this.
|
|
18164
|
+
const newKey = await this.putUnchecked(params);
|
|
18098
18165
|
for (const row of existing) {
|
|
18099
18166
|
if (row.id === newKey) continue;
|
|
18100
18167
|
const rowData = row.data;
|
|
@@ -18137,8 +18204,11 @@ var MediaStore = class {
|
|
|
18137
18204
|
* own kind.
|
|
18138
18205
|
*/
|
|
18139
18206
|
async promoteToLastFrame(input) {
|
|
18207
|
+
return this.gated(() => this.promoteToLastFrameUnchecked(input));
|
|
18208
|
+
}
|
|
18209
|
+
async promoteToLastFrameUnchecked(input) {
|
|
18140
18210
|
const data = Buffer.from(input.snapshot.base64, "base64");
|
|
18141
|
-
const newKey = await this.
|
|
18211
|
+
const newKey = await this.putReplacingUnchecked({
|
|
18142
18212
|
deviceId: input.deviceId,
|
|
18143
18213
|
ownerKind: "track",
|
|
18144
18214
|
ownerId: input.trackId,
|
|
@@ -18146,7 +18216,7 @@ var MediaStore = class {
|
|
|
18146
18216
|
timestamp: input.snapshot.timestamp,
|
|
18147
18217
|
data
|
|
18148
18218
|
});
|
|
18149
|
-
if (input.keepSource !== true) await this.
|
|
18219
|
+
if (input.keepSource !== true) await this.deleteByKeyUnchecked(input.snapshot.key);
|
|
18150
18220
|
return newKey;
|
|
18151
18221
|
}
|
|
18152
18222
|
/**
|
|
@@ -18184,6 +18254,9 @@ var MediaStore = class {
|
|
|
18184
18254
|
* Deletes the blob from storage (best-effort) and removes the index row.
|
|
18185
18255
|
*/
|
|
18186
18256
|
async deleteByKey(key) {
|
|
18257
|
+
await this.gated(() => this.deleteByKeyUnchecked(key));
|
|
18258
|
+
}
|
|
18259
|
+
async deleteByKeyUnchecked(key) {
|
|
18187
18260
|
const row = await this.store.get.query({
|
|
18188
18261
|
collection: MEDIA_COLLECTION,
|
|
18189
18262
|
key
|
|
@@ -18294,6 +18367,9 @@ var MediaStore = class {
|
|
|
18294
18367
|
* Best-effort per row; returns the number of index rows removed.
|
|
18295
18368
|
*/
|
|
18296
18369
|
async deleteForOwner(ownerKind, ownerIds) {
|
|
18370
|
+
return this.gated(() => this.deleteForOwnerUnchecked(ownerKind, ownerIds));
|
|
18371
|
+
}
|
|
18372
|
+
async deleteForOwnerUnchecked(ownerKind, ownerIds) {
|
|
18297
18373
|
let removed = 0;
|
|
18298
18374
|
for (const ownerId of ownerIds) {
|
|
18299
18375
|
const rows = await this.store.query.query({
|
|
@@ -18375,6 +18451,9 @@ var MediaStore = class {
|
|
|
18375
18451
|
* The old-entry deletion is best-effort: on failure a warning is logged
|
|
18376
18452
|
* and the method still returns the new key. */
|
|
18377
18453
|
async reown(input) {
|
|
18454
|
+
return this.gated(() => this.reownUnchecked(input));
|
|
18455
|
+
}
|
|
18456
|
+
async reownUnchecked(input) {
|
|
18378
18457
|
const { fromOwnerKind, fromOwnerId, toOwnerKind, toOwnerId, key } = input;
|
|
18379
18458
|
const row = (await this.store.query.query({
|
|
18380
18459
|
collection: MEDIA_COLLECTION,
|
|
@@ -18408,7 +18487,7 @@ var MediaStore = class {
|
|
|
18408
18487
|
relativePath: oldPath
|
|
18409
18488
|
})
|
|
18410
18489
|
};
|
|
18411
|
-
const newKey = await this.
|
|
18490
|
+
const newKey = await this.putUnchecked(newParams);
|
|
18412
18491
|
try {
|
|
18413
18492
|
await this.storage.delete({
|
|
18414
18493
|
location: mediaRowLocation(meta),
|
|
@@ -18462,8 +18541,10 @@ var MediaStore = class {
|
|
|
18462
18541
|
* batch.
|
|
18463
18542
|
*/
|
|
18464
18543
|
async deleteByTracks(trackIds) {
|
|
18465
|
-
await this.
|
|
18466
|
-
|
|
18544
|
+
await this.gated(async () => {
|
|
18545
|
+
await this.deleteForOwnerUnchecked("track", trackIds);
|
|
18546
|
+
await this.deleteForOwnerUnchecked("face", trackIds.map((id) => FACE_MEDIA_OWNER_PREFIX + id));
|
|
18547
|
+
});
|
|
18467
18548
|
await this.deleteForOwner("plate", trackIds.map((id) => PLATE_MEDIA_OWNER_PREFIX + id));
|
|
18468
18549
|
}
|
|
18469
18550
|
/**
|
|
@@ -18505,7 +18586,7 @@ function snapshot(j) {
|
|
|
18505
18586
|
fromLocationId: j.fromLocationId,
|
|
18506
18587
|
toLocationId: j.toLocationId,
|
|
18507
18588
|
deviceId: j.deviceId,
|
|
18508
|
-
entities: ["media"],
|
|
18589
|
+
entities: ["media", "retrainFrames"],
|
|
18509
18590
|
filesMoved: j.filesMoved,
|
|
18510
18591
|
bytesMoved: j.bytesMoved,
|
|
18511
18592
|
filesTotal: null,
|
|
@@ -18536,7 +18617,7 @@ var MediaRelocateEngine = class {
|
|
|
18536
18617
|
state: "running",
|
|
18537
18618
|
fromLocationId: "*",
|
|
18538
18619
|
toLocationId: input.toLocationId,
|
|
18539
|
-
deviceId:
|
|
18620
|
+
deviceId: null,
|
|
18540
18621
|
filesMoved: 0,
|
|
18541
18622
|
bytesMoved: 0,
|
|
18542
18623
|
startedAt: this.deps.now(),
|
|
@@ -18553,24 +18634,25 @@ var MediaRelocateEngine = class {
|
|
|
18553
18634
|
const bytesPerMs = throttleMbps * 1024 * 1024 / 1e3;
|
|
18554
18635
|
try {
|
|
18555
18636
|
await this.deps.resolveTargetRoot(job.toLocationId);
|
|
18637
|
+
let failedRows = 0;
|
|
18556
18638
|
let cursor = 0;
|
|
18557
|
-
let
|
|
18639
|
+
let offsetAtCursor = 0;
|
|
18558
18640
|
for (;;) {
|
|
18559
18641
|
if (job.cancelRequested) break;
|
|
18560
|
-
const
|
|
18642
|
+
const rows = await this.deps.store.query.query({
|
|
18561
18643
|
collection: MEDIA_COLLECTION,
|
|
18562
18644
|
filter: {
|
|
18563
|
-
...job.deviceId !== null ? { where: { deviceId: job.deviceId } } : {},
|
|
18564
18645
|
whereBetween: { timestamp: [cursor, Number.MAX_SAFE_INTEGER] },
|
|
18565
18646
|
orderBy: {
|
|
18566
18647
|
field: "timestamp",
|
|
18567
18648
|
direction: "asc"
|
|
18568
18649
|
},
|
|
18569
|
-
limit: PAGE_SIZE
|
|
18650
|
+
limit: PAGE_SIZE,
|
|
18651
|
+
offset: offsetAtCursor
|
|
18570
18652
|
}
|
|
18571
|
-
})
|
|
18572
|
-
if (
|
|
18573
|
-
for (const row of
|
|
18653
|
+
});
|
|
18654
|
+
if (rows.length === 0) break;
|
|
18655
|
+
for (const row of rows) {
|
|
18574
18656
|
if (job.cancelRequested) break;
|
|
18575
18657
|
const data = row.data;
|
|
18576
18658
|
const from = mediaRowLocation(data);
|
|
@@ -18578,6 +18660,17 @@ var MediaRelocateEngine = class {
|
|
|
18578
18660
|
const relativePath = String(data["path"] ?? "");
|
|
18579
18661
|
if (relativePath.length === 0) continue;
|
|
18580
18662
|
try {
|
|
18663
|
+
if (await this.samePhysicalLocation(from, job.toLocationId)) {
|
|
18664
|
+
await this.deps.store.set.mutate({
|
|
18665
|
+
collection: MEDIA_COLLECTION,
|
|
18666
|
+
key: row.id,
|
|
18667
|
+
value: {
|
|
18668
|
+
...data,
|
|
18669
|
+
locationId: job.toLocationId
|
|
18670
|
+
}
|
|
18671
|
+
});
|
|
18672
|
+
continue;
|
|
18673
|
+
}
|
|
18581
18674
|
const bytes = await this.deps.storage.read({
|
|
18582
18675
|
location: from,
|
|
18583
18676
|
relativePath
|
|
@@ -18587,6 +18680,10 @@ var MediaRelocateEngine = class {
|
|
|
18587
18680
|
relativePath,
|
|
18588
18681
|
data: bytes
|
|
18589
18682
|
});
|
|
18683
|
+
if ((await this.deps.storage.read({
|
|
18684
|
+
location: job.toLocationId,
|
|
18685
|
+
relativePath
|
|
18686
|
+
})).length !== bytes.length) throw new Error(`relocated media size mismatch for ${relativePath}`);
|
|
18590
18687
|
await this.deps.store.set.mutate({
|
|
18591
18688
|
collection: MEDIA_COLLECTION,
|
|
18592
18689
|
key: row.id,
|
|
@@ -18603,20 +18700,99 @@ var MediaRelocateEngine = class {
|
|
|
18603
18700
|
job.bytesMoved += bytes.length;
|
|
18604
18701
|
await sleep(bytes.length / bytesPerMs);
|
|
18605
18702
|
} catch (err) {
|
|
18703
|
+
failedRows++;
|
|
18606
18704
|
this.deps.logger.debug("media relocate row failed", { meta: {
|
|
18607
18705
|
key: row.id,
|
|
18608
18706
|
error: String(err)
|
|
18609
18707
|
} });
|
|
18610
18708
|
}
|
|
18611
18709
|
}
|
|
18612
|
-
const last =
|
|
18710
|
+
const last = rows[rows.length - 1];
|
|
18613
18711
|
const lastTs = Number(last.data["timestamp"] ?? cursor);
|
|
18614
|
-
if (lastTs === cursor)
|
|
18712
|
+
if (lastTs === cursor) offsetAtCursor += rows.length;
|
|
18615
18713
|
else {
|
|
18616
18714
|
cursor = lastTs;
|
|
18617
|
-
|
|
18715
|
+
offsetAtCursor = rows.filter((row) => Number(row.data["timestamp"]) === lastTs).length;
|
|
18618
18716
|
}
|
|
18717
|
+
if (rows.length < PAGE_SIZE) break;
|
|
18619
18718
|
}
|
|
18719
|
+
let retrainOffset = 0;
|
|
18720
|
+
for (;;) {
|
|
18721
|
+
if (job.cancelRequested) break;
|
|
18722
|
+
const rows = await this.deps.store.query.query({
|
|
18723
|
+
collection: RETRAIN_FRAMES_COLLECTION,
|
|
18724
|
+
filter: {
|
|
18725
|
+
orderBy: {
|
|
18726
|
+
field: "copiedAt",
|
|
18727
|
+
direction: "asc"
|
|
18728
|
+
},
|
|
18729
|
+
limit: PAGE_SIZE,
|
|
18730
|
+
offset: retrainOffset
|
|
18731
|
+
}
|
|
18732
|
+
});
|
|
18733
|
+
if (rows.length === 0) break;
|
|
18734
|
+
for (const row of rows) {
|
|
18735
|
+
if (job.cancelRequested) break;
|
|
18736
|
+
const data = row.data;
|
|
18737
|
+
const from = mediaRowLocation(data);
|
|
18738
|
+
if (from === job.toLocationId) continue;
|
|
18739
|
+
const relativePath = String(data["path"] ?? "");
|
|
18740
|
+
if (relativePath.length === 0) {
|
|
18741
|
+
failedRows++;
|
|
18742
|
+
continue;
|
|
18743
|
+
}
|
|
18744
|
+
try {
|
|
18745
|
+
if (await this.samePhysicalLocation(from, job.toLocationId)) {
|
|
18746
|
+
await this.deps.store.set.mutate({
|
|
18747
|
+
collection: RETRAIN_FRAMES_COLLECTION,
|
|
18748
|
+
key: row.id,
|
|
18749
|
+
value: {
|
|
18750
|
+
...data,
|
|
18751
|
+
locationId: job.toLocationId
|
|
18752
|
+
}
|
|
18753
|
+
});
|
|
18754
|
+
continue;
|
|
18755
|
+
}
|
|
18756
|
+
const bytes = await this.deps.storage.read({
|
|
18757
|
+
location: from,
|
|
18758
|
+
relativePath
|
|
18759
|
+
});
|
|
18760
|
+
await this.deps.storage.write({
|
|
18761
|
+
location: job.toLocationId,
|
|
18762
|
+
relativePath,
|
|
18763
|
+
data: bytes
|
|
18764
|
+
});
|
|
18765
|
+
if ((await this.deps.storage.read({
|
|
18766
|
+
location: job.toLocationId,
|
|
18767
|
+
relativePath
|
|
18768
|
+
})).length !== bytes.length) throw new Error(`relocated retrain frame size mismatch for ${relativePath}`);
|
|
18769
|
+
await this.deps.store.set.mutate({
|
|
18770
|
+
collection: RETRAIN_FRAMES_COLLECTION,
|
|
18771
|
+
key: row.id,
|
|
18772
|
+
value: {
|
|
18773
|
+
...data,
|
|
18774
|
+
locationId: job.toLocationId
|
|
18775
|
+
}
|
|
18776
|
+
});
|
|
18777
|
+
await this.deps.storage.delete({
|
|
18778
|
+
location: from,
|
|
18779
|
+
relativePath
|
|
18780
|
+
});
|
|
18781
|
+
job.filesMoved++;
|
|
18782
|
+
job.bytesMoved += bytes.length;
|
|
18783
|
+
await sleep(bytes.length / bytesPerMs);
|
|
18784
|
+
} catch (err) {
|
|
18785
|
+
failedRows++;
|
|
18786
|
+
this.deps.logger.debug("retrain frame relocate row failed", { meta: {
|
|
18787
|
+
key: row.id,
|
|
18788
|
+
error: String(err)
|
|
18789
|
+
} });
|
|
18790
|
+
}
|
|
18791
|
+
}
|
|
18792
|
+
retrainOffset += rows.length;
|
|
18793
|
+
if (rows.length < PAGE_SIZE) break;
|
|
18794
|
+
}
|
|
18795
|
+
if (failedRows > 0) throw new Error(`event-media relocation left ${failedRows} row(s) unmoved`);
|
|
18620
18796
|
job.state = job.cancelRequested ? "cancelled" : "done";
|
|
18621
18797
|
} catch (err) {
|
|
18622
18798
|
job.state = "failed";
|
|
@@ -18630,6 +18806,18 @@ var MediaRelocateEngine = class {
|
|
|
18630
18806
|
this.deps.onFinished?.(snapshot(job));
|
|
18631
18807
|
}
|
|
18632
18808
|
}
|
|
18809
|
+
async samePhysicalLocation(from, to) {
|
|
18810
|
+
if (from === to) return true;
|
|
18811
|
+
try {
|
|
18812
|
+
const [fromRoot, toRoot] = await Promise.all([this.deps.resolveTargetRoot(from), this.deps.resolveTargetRoot(to)]);
|
|
18813
|
+
const [fromStat, toStat] = await Promise.all([node_fs.promises.stat(fromRoot), node_fs.promises.stat(toRoot)]);
|
|
18814
|
+
if (fromStat.dev === toStat.dev && fromStat.ino === toStat.ino) return true;
|
|
18815
|
+
const [fromReal, toReal] = await Promise.all([node_fs.promises.realpath(node_path.default.resolve(fromRoot)), node_fs.promises.realpath(node_path.default.resolve(toRoot))]);
|
|
18816
|
+
return fromReal === toReal;
|
|
18817
|
+
} catch {
|
|
18818
|
+
return false;
|
|
18819
|
+
}
|
|
18820
|
+
}
|
|
18633
18821
|
};
|
|
18634
18822
|
//#endregion
|
|
18635
18823
|
//#region src/pipeline-analytics/media-settings.ts
|
|
@@ -31182,6 +31370,9 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
|
|
|
31182
31370
|
eventsOpsLog = null;
|
|
31183
31371
|
/** Event-media relocation engine (entity-routing Phase 4). */
|
|
31184
31372
|
mediaRelocate = null;
|
|
31373
|
+
mediaLocationStorage = null;
|
|
31374
|
+
mediaWriteGate = new MediaWriteGate();
|
|
31375
|
+
storageMigrationLeaseId = null;
|
|
31185
31376
|
/** Per-camera history of LINKED-device sensor state changes (Part B). */
|
|
31186
31377
|
sensorEventStore = null;
|
|
31187
31378
|
/** Ingest-side reverse index (sensor device → linked camera ids), TTL-cached
|
|
@@ -31982,7 +32173,7 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
|
|
|
31982
32173
|
logger.info("pipeline-analytics: event media rooted at CAMSTACK_MEDIA_ROOT", { meta: { mediaRoot } });
|
|
31983
32174
|
}
|
|
31984
32175
|
if (!storage) throw new Error("pipeline-analytics requires ctx.kernel.storage");
|
|
31985
|
-
|
|
32176
|
+
const routed = createLocationAwareMediaStorage({
|
|
31986
32177
|
base: storage,
|
|
31987
32178
|
defaultLocation: "eventMedia",
|
|
31988
32179
|
resolveRoot: (locationId) => this.ctx.api.storage.resolve.query({
|
|
@@ -31990,6 +32181,8 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
|
|
|
31990
32181
|
relativePath: ""
|
|
31991
32182
|
})
|
|
31992
32183
|
});
|
|
32184
|
+
this.mediaLocationStorage = routed;
|
|
32185
|
+
return routed;
|
|
31993
32186
|
}
|
|
31994
32187
|
/** Constructs every SQLite-backed store plus the stationary/package-drop/
|
|
31995
32188
|
* sensor plumbing, in the exact pre-S7 order. Returns the non-null bundle
|
|
@@ -32034,7 +32227,8 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
|
|
|
32034
32227
|
const mediaStore = new MediaStore({
|
|
32035
32228
|
storage,
|
|
32036
32229
|
store: api.settingsStore,
|
|
32037
|
-
logger: logger.child("MediaStore")
|
|
32230
|
+
logger: logger.child("MediaStore"),
|
|
32231
|
+
writeGate: this.mediaWriteGate
|
|
32038
32232
|
});
|
|
32039
32233
|
this.mediaStore = mediaStore;
|
|
32040
32234
|
const retrainFrames = new RetrainFrameStore({
|
|
@@ -32042,6 +32236,7 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
|
|
|
32042
32236
|
storage,
|
|
32043
32237
|
logger: logger.child("RetrainFrames"),
|
|
32044
32238
|
now: () => Date.now(),
|
|
32239
|
+
writeGate: this.mediaWriteGate,
|
|
32045
32240
|
readSource: async (mediaKey) => {
|
|
32046
32241
|
const file = await mediaStore.getByKey(mediaKey);
|
|
32047
32242
|
return file === null ? null : Buffer.from(file.base64, "base64");
|
|
@@ -36283,14 +36478,38 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
|
|
|
36283
36478
|
return this.queryFacade.deleteDeviceEvents(input);
|
|
36284
36479
|
}
|
|
36285
36480
|
/** The events ops-log rows (newest-first), optionally scoped to one camera. */
|
|
36286
|
-
async
|
|
36481
|
+
async pauseForStorageMigration(input) {
|
|
36482
|
+
if (this.storageMigrationLeaseId !== null && this.storageMigrationLeaseId !== input.leaseId) throw new Error("event-media writes are already paused by another storage migration");
|
|
36483
|
+
this.storageMigrationLeaseId = input.leaseId;
|
|
36484
|
+
try {
|
|
36485
|
+
await this.mediaWriteGate.pause();
|
|
36486
|
+
return { paused: true };
|
|
36487
|
+
} catch (err) {
|
|
36488
|
+
this.storageMigrationLeaseId = null;
|
|
36489
|
+
throw err;
|
|
36490
|
+
}
|
|
36491
|
+
}
|
|
36492
|
+
async resumeForStorageMigration(input) {
|
|
36493
|
+
if (this.storageMigrationLeaseId === null) return { resumed: true };
|
|
36494
|
+
if (this.storageMigrationLeaseId !== input.leaseId) throw new Error("event-media storage migration lease does not match");
|
|
36495
|
+
this.mediaWriteGate.resume();
|
|
36496
|
+
this.storageMigrationLeaseId = null;
|
|
36497
|
+
return { resumed: true };
|
|
36498
|
+
}
|
|
36499
|
+
async refreshStorageLocationsForMigration(input) {
|
|
36500
|
+
if (this.storageMigrationLeaseId !== input.leaseId) throw new Error("event-media storage migration lease does not match");
|
|
36501
|
+
this.mediaLocationStorage?.invalidateRoots();
|
|
36502
|
+
return { refreshed: true };
|
|
36503
|
+
}
|
|
36504
|
+
async startStorageMigrationMove(input) {
|
|
36287
36505
|
if (!this.mediaRelocate) throw new Error("media relocation unavailable");
|
|
36506
|
+
if (this.storageMigrationLeaseId !== input.leaseId) throw new Error("event-media storage migration lease does not match");
|
|
36288
36507
|
return { jobId: this.mediaRelocate.start(input) };
|
|
36289
36508
|
}
|
|
36290
|
-
async
|
|
36291
|
-
return this.mediaRelocate?.list() ??
|
|
36509
|
+
async getStorageMigrationMoveStatus(input) {
|
|
36510
|
+
return this.mediaRelocate?.list().find((job) => job.jobId === input.jobId) ?? null;
|
|
36292
36511
|
}
|
|
36293
|
-
async
|
|
36512
|
+
async cancelStorageMigrationMove(input) {
|
|
36294
36513
|
return { cancelled: this.mediaRelocate?.cancel(input.jobId) ?? false };
|
|
36295
36514
|
}
|
|
36296
36515
|
async listOpsLog(input) {
|