@camstack/addon-post-analysis 1.2.144 → 1.2.147
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-C8RL0Hzy.js → dist-1xYuXmDS.js} +131 -12
- package/dist/{dist-3FJkk7Mx.mjs → dist-4eP-xmjY.mjs} +131 -12
- 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-Bb5cXFSY.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-B8VSQDMs.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-DC69_fX2.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-C7KsMvFR.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-Btr3CZPD.mjs → hostInit-Dt9TGWyM.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +189 -126
- package/dist/pipeline-analytics/index.mjs +189 -126
- 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-1xYuXmDS.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");
|
|
@@ -40213,9 +40213,6 @@ function readJpegSize(data) {
|
|
|
40213
40213
|
* table or the disk it consumes."
|
|
40214
40214
|
*/
|
|
40215
40215
|
var RETRAIN_FRAMES_COLLECTION = "pipeline-analytics:retrain-frames";
|
|
40216
|
-
/** Storage location for retrain copies — the same default media root the event
|
|
40217
|
-
* media lives on, under a prefix nothing else enumerates. */
|
|
40218
|
-
var RETRAIN_MEDIA_LOCATION = "eventMedia";
|
|
40219
40216
|
/** Path prefix of every retrain copy. Never `<deviceId>/events/…`: the orphan
|
|
40220
40217
|
* audit resolves ownership from INDEX rows, but a future filesystem-side tool
|
|
40221
40218
|
* must be able to tell the two corpora apart by path alone. */
|
|
@@ -40308,6 +40305,7 @@ var RetrainFrameStore = class {
|
|
|
40308
40305
|
readSource;
|
|
40309
40306
|
now;
|
|
40310
40307
|
writeGate;
|
|
40308
|
+
resolveEventMediaLocation;
|
|
40311
40309
|
constructor(deps) {
|
|
40312
40310
|
this.store = deps.store;
|
|
40313
40311
|
this.storage = deps.storage;
|
|
@@ -40315,6 +40313,29 @@ var RetrainFrameStore = class {
|
|
|
40315
40313
|
this.readSource = deps.readSource;
|
|
40316
40314
|
this.now = deps.now;
|
|
40317
40315
|
this.writeGate = deps.writeGate;
|
|
40316
|
+
this.resolveEventMediaLocation = deps.resolveEventMediaLocation;
|
|
40317
|
+
}
|
|
40318
|
+
/**
|
|
40319
|
+
* The location a NEW copy is written to, and the id its row NAMES.
|
|
40320
|
+
*
|
|
40321
|
+
* `null` = "no concrete location was resolved", which persists as the legacy
|
|
40322
|
+
* NULL row. A resolver that throws must not refuse the copy — losing an
|
|
40323
|
+
* operator's selection is worse than an unstamped row the seal can fix — but
|
|
40324
|
+
* it says so, because that branch re-creates the exact population a
|
|
40325
|
+
* non-blocking migration is gated on.
|
|
40326
|
+
*/
|
|
40327
|
+
async resolveWriteLocation(deviceId) {
|
|
40328
|
+
if (!this.resolveEventMediaLocation) return null;
|
|
40329
|
+
try {
|
|
40330
|
+
const resolved = await this.resolveEventMediaLocation(deviceId);
|
|
40331
|
+
return resolved === "eventMedia" ? null : resolved;
|
|
40332
|
+
} catch (err) {
|
|
40333
|
+
this.logger.warn("retrain copy could not resolve its eventMedia location — the row is written UNSTAMPED and a storage repoint would re-aim it at a disk that does not hold it", {
|
|
40334
|
+
tags: { deviceId },
|
|
40335
|
+
meta: { error: String(err) }
|
|
40336
|
+
});
|
|
40337
|
+
return null;
|
|
40338
|
+
}
|
|
40318
40339
|
}
|
|
40319
40340
|
static async declare(store) {
|
|
40320
40341
|
await store.declareCollection.mutate({
|
|
@@ -40473,6 +40494,7 @@ var RetrainFrameStore = class {
|
|
|
40473
40494
|
};
|
|
40474
40495
|
}
|
|
40475
40496
|
const path = framePath(input.deviceId, input.trackId, frameId);
|
|
40497
|
+
const locationId = await this.resolveWriteLocation(input.deviceId);
|
|
40476
40498
|
const frame = {
|
|
40477
40499
|
frameId,
|
|
40478
40500
|
deviceId: input.deviceId,
|
|
@@ -40483,11 +40505,11 @@ var RetrainFrameStore = class {
|
|
|
40483
40505
|
width: size.width,
|
|
40484
40506
|
height: size.height,
|
|
40485
40507
|
copiedAt: this.now(),
|
|
40486
|
-
locationId
|
|
40508
|
+
locationId
|
|
40487
40509
|
};
|
|
40488
40510
|
const persist = async () => {
|
|
40489
40511
|
await this.storage.write({
|
|
40490
|
-
location:
|
|
40512
|
+
location: locationId ?? "eventMedia",
|
|
40491
40513
|
relativePath: path,
|
|
40492
40514
|
data: bytes
|
|
40493
40515
|
});
|
|
@@ -40504,7 +40526,7 @@ var RetrainFrameStore = class {
|
|
|
40504
40526
|
width: frame.width,
|
|
40505
40527
|
height: frame.height,
|
|
40506
40528
|
copiedAt: frame.copiedAt,
|
|
40507
|
-
locationId:
|
|
40529
|
+
locationId: frame.locationId
|
|
40508
40530
|
}
|
|
40509
40531
|
});
|
|
40510
40532
|
};
|
|
@@ -43503,6 +43525,13 @@ function pickWritableMediaLocation(locations, currentDefaultId) {
|
|
|
43503
43525
|
//#region src/pipeline-analytics/media-relocate-engine.ts
|
|
43504
43526
|
var PAGE_SIZE = 200;
|
|
43505
43527
|
var DEFAULT_THROTTLE_MBPS = 40;
|
|
43528
|
+
/** What a pass touches, reported on the job so an ops-log row says which of the
|
|
43529
|
+
* three passes ran without anyone reading this file. */
|
|
43530
|
+
function entitiesFor(mode) {
|
|
43531
|
+
if (mode === "seal") return ["mediaRows", "retrainFrameRows"];
|
|
43532
|
+
if (mode === "gallery") return ["galleryMedia"];
|
|
43533
|
+
return ["media", "retrainFrames"];
|
|
43534
|
+
}
|
|
43506
43535
|
function snapshot(j) {
|
|
43507
43536
|
return {
|
|
43508
43537
|
jobId: j.jobId,
|
|
@@ -43510,7 +43539,7 @@ function snapshot(j) {
|
|
|
43510
43539
|
fromLocationId: j.fromLocationId,
|
|
43511
43540
|
toLocationId: j.toLocationId,
|
|
43512
43541
|
deviceId: j.deviceId,
|
|
43513
|
-
entities: [
|
|
43542
|
+
entities: [...entitiesFor(j.mode)],
|
|
43514
43543
|
filesMoved: j.filesMoved,
|
|
43515
43544
|
bytesMoved: j.bytesMoved,
|
|
43516
43545
|
filesTotal: null,
|
|
@@ -43519,6 +43548,22 @@ function snapshot(j) {
|
|
|
43519
43548
|
error: j.error
|
|
43520
43549
|
};
|
|
43521
43550
|
}
|
|
43551
|
+
/**
|
|
43552
|
+
* True when the row names NO location — the population `seal` exists for.
|
|
43553
|
+
*
|
|
43554
|
+
* Deliberately NOT `mediaRowLocation(data) === DEFAULT_MEDIA_LOCATION`: that
|
|
43555
|
+
* helper collapses "unstamped" and "stamped with the bare type ref" into one
|
|
43556
|
+
* answer, and only the first is a row whose meaning changes under a repoint.
|
|
43557
|
+
*/
|
|
43558
|
+
function rowIsUnstamped(data) {
|
|
43559
|
+
const id = data["locationId"];
|
|
43560
|
+
return !(typeof id === "string" && id.length > 0);
|
|
43561
|
+
}
|
|
43562
|
+
/** True for the enrolled-gallery rows `move` skips and `gallery` selects. */
|
|
43563
|
+
function rowIsGallery(data) {
|
|
43564
|
+
const ownerKind = String(data["ownerKind"] ?? "");
|
|
43565
|
+
return isMediaOwnerKind(ownerKind) && isRetentionExemptOwnerKind(ownerKind);
|
|
43566
|
+
}
|
|
43522
43567
|
var MediaRelocateEngine = class {
|
|
43523
43568
|
deps;
|
|
43524
43569
|
jobs = /* @__PURE__ */ new Map();
|
|
@@ -43534,11 +43579,34 @@ var MediaRelocateEngine = class {
|
|
|
43534
43579
|
job.cancelRequested = true;
|
|
43535
43580
|
return true;
|
|
43536
43581
|
}
|
|
43582
|
+
/**
|
|
43583
|
+
* How many rows still carry no `locationId`, per collection.
|
|
43584
|
+
*
|
|
43585
|
+
* Read-only, and the ONLY number the migration planner's non-blocking gate
|
|
43586
|
+
* is allowed to trust: it is counted here, against the rows themselves,
|
|
43587
|
+
* rather than inferred from "a seal job reported done" — a seal that failed
|
|
43588
|
+
* some rows still reports a terminal state.
|
|
43589
|
+
*/
|
|
43590
|
+
async countUnstamped() {
|
|
43591
|
+
const media = await this.countUnstampedIn(MEDIA_COLLECTION, "timestamp");
|
|
43592
|
+
const retrainFrames = await this.countUnstampedIn(RETRAIN_FRAMES_COLLECTION, "copiedAt");
|
|
43593
|
+
return {
|
|
43594
|
+
media,
|
|
43595
|
+
retrainFrames,
|
|
43596
|
+
total: media + retrainFrames
|
|
43597
|
+
};
|
|
43598
|
+
}
|
|
43599
|
+
async countUnstampedIn(collection, orderField) {
|
|
43600
|
+
let unstamped = 0;
|
|
43601
|
+
for await (const row of this.pageRows(collection, orderField)) if (rowIsUnstamped(row.data)) unstamped++;
|
|
43602
|
+
return unstamped;
|
|
43603
|
+
}
|
|
43537
43604
|
start(input) {
|
|
43538
43605
|
for (const j of this.jobs.values()) if (j.state === "running") throw new Error(`a media relocation is already running (${j.jobId})`);
|
|
43539
43606
|
const job = {
|
|
43540
43607
|
jobId: this.deps.newId(),
|
|
43541
43608
|
state: "running",
|
|
43609
|
+
mode: input.mode ?? "move",
|
|
43542
43610
|
fromLocationId: "*",
|
|
43543
43611
|
toLocationId: input.toLocationId,
|
|
43544
43612
|
deviceId: null,
|
|
@@ -43553,130 +43621,104 @@ var MediaRelocateEngine = class {
|
|
|
43553
43621
|
this.run(job, input.throttleMbps ?? DEFAULT_THROTTLE_MBPS);
|
|
43554
43622
|
return job.jobId;
|
|
43555
43623
|
}
|
|
43624
|
+
/**
|
|
43625
|
+
* Page one collection oldest-first, yielding every row exactly once.
|
|
43626
|
+
*
|
|
43627
|
+
* The cursor is `(orderField, offset-within-ties)` rather than a plain
|
|
43628
|
+
* offset: 201 rows sharing one timestamp is a real shape here (a burst of
|
|
43629
|
+
* snapshots for one track), and a plain `offset` walk over a collection being
|
|
43630
|
+
* mutated underneath is how rows get skipped. Rows only ever gain a
|
|
43631
|
+
* `locationId` during a pass — never a new order key — so appending inserts
|
|
43632
|
+
* land after the cursor and are picked up, and nothing is missed.
|
|
43633
|
+
*/
|
|
43634
|
+
async *pageRows(collection, orderField) {
|
|
43635
|
+
let cursor = 0;
|
|
43636
|
+
let offsetAtCursor = 0;
|
|
43637
|
+
for (;;) {
|
|
43638
|
+
const rows = await this.deps.store.query.query({
|
|
43639
|
+
collection,
|
|
43640
|
+
filter: {
|
|
43641
|
+
whereBetween: { [orderField]: [cursor, Number.MAX_SAFE_INTEGER] },
|
|
43642
|
+
orderBy: {
|
|
43643
|
+
field: orderField,
|
|
43644
|
+
direction: "asc"
|
|
43645
|
+
},
|
|
43646
|
+
limit: PAGE_SIZE,
|
|
43647
|
+
offset: offsetAtCursor
|
|
43648
|
+
}
|
|
43649
|
+
});
|
|
43650
|
+
if (rows.length === 0) return;
|
|
43651
|
+
for (const row of rows) yield row;
|
|
43652
|
+
const last = rows[rows.length - 1];
|
|
43653
|
+
if (last === void 0) return;
|
|
43654
|
+
const lastKey = Number(last.data[orderField] ?? cursor);
|
|
43655
|
+
if (lastKey === cursor) offsetAtCursor += rows.length;
|
|
43656
|
+
else {
|
|
43657
|
+
cursor = lastKey;
|
|
43658
|
+
offsetAtCursor = rows.filter((row) => Number(row.data[orderField]) === lastKey).length;
|
|
43659
|
+
}
|
|
43660
|
+
if (rows.length < PAGE_SIZE) return;
|
|
43661
|
+
}
|
|
43662
|
+
}
|
|
43663
|
+
/** Does this pass act on this row at all? */
|
|
43664
|
+
selects(job, data, isRetrain) {
|
|
43665
|
+
if (job.mode === "seal") return rowIsUnstamped(data);
|
|
43666
|
+
if (mediaRowLocation(data) === job.toLocationId) return false;
|
|
43667
|
+
if (job.mode === "gallery") return !isRetrain && rowIsGallery(data);
|
|
43668
|
+
return isRetrain || !rowIsGallery(data);
|
|
43669
|
+
}
|
|
43556
43670
|
async run(job, throttleMbps) {
|
|
43557
43671
|
const sleep = this.deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
43558
43672
|
const bytesPerMs = throttleMbps * 1024 * 1024 / 1e3;
|
|
43559
43673
|
try {
|
|
43560
43674
|
await this.deps.resolveTargetRoot(job.toLocationId);
|
|
43561
43675
|
let failedRows = 0;
|
|
43562
|
-
|
|
43563
|
-
|
|
43564
|
-
|
|
43676
|
+
/** Selected rows that name no blob path. Counted and reported ONCE per
|
|
43677
|
+
* pass rather than per row: a row with no path names no bytes anywhere,
|
|
43678
|
+
* so there is nothing a repoint can orphan — but a pass that steps over
|
|
43679
|
+
* rows in silence reads as "there were none", which is how a migration
|
|
43680
|
+
* gets believed. */
|
|
43681
|
+
let pathlessRows = 0;
|
|
43682
|
+
const collections = job.mode === "gallery" ? [{
|
|
43683
|
+
name: MEDIA_COLLECTION,
|
|
43684
|
+
order: "timestamp"
|
|
43685
|
+
}] : [{
|
|
43686
|
+
name: MEDIA_COLLECTION,
|
|
43687
|
+
order: "timestamp"
|
|
43688
|
+
}, {
|
|
43689
|
+
name: RETRAIN_FRAMES_COLLECTION,
|
|
43690
|
+
order: "copiedAt"
|
|
43691
|
+
}];
|
|
43692
|
+
for (const collection of collections) {
|
|
43565
43693
|
if (job.cancelRequested) break;
|
|
43566
|
-
const
|
|
43567
|
-
|
|
43568
|
-
filter: {
|
|
43569
|
-
whereBetween: { timestamp: [cursor, Number.MAX_SAFE_INTEGER] },
|
|
43570
|
-
orderBy: {
|
|
43571
|
-
field: "timestamp",
|
|
43572
|
-
direction: "asc"
|
|
43573
|
-
},
|
|
43574
|
-
limit: PAGE_SIZE,
|
|
43575
|
-
offset: offsetAtCursor
|
|
43576
|
-
}
|
|
43577
|
-
});
|
|
43578
|
-
if (rows.length === 0) break;
|
|
43579
|
-
for (const row of rows) {
|
|
43694
|
+
const isRetrain = collection.name === RETRAIN_FRAMES_COLLECTION;
|
|
43695
|
+
for await (const row of this.pageRows(collection.name, collection.order)) {
|
|
43580
43696
|
if (job.cancelRequested) break;
|
|
43581
43697
|
const data = row.data;
|
|
43582
|
-
|
|
43583
|
-
if (
|
|
43584
|
-
|
|
43585
|
-
|
|
43586
|
-
|
|
43587
|
-
|
|
43588
|
-
|
|
43589
|
-
|
|
43590
|
-
|
|
43591
|
-
collection: MEDIA_COLLECTION,
|
|
43698
|
+
if (!this.selects(job, data, isRetrain)) continue;
|
|
43699
|
+
if (job.mode === "seal") {
|
|
43700
|
+
try {
|
|
43701
|
+
await this.stamp(collection.name, row.id, data, job.toLocationId);
|
|
43702
|
+
job.filesMoved++;
|
|
43703
|
+
} catch (err) {
|
|
43704
|
+
failedRows++;
|
|
43705
|
+
this.deps.logger.debug("event-media seal row failed", { meta: {
|
|
43706
|
+
collection: collection.name,
|
|
43592
43707
|
key: row.id,
|
|
43593
|
-
|
|
43594
|
-
|
|
43595
|
-
locationId: job.toLocationId
|
|
43596
|
-
}
|
|
43597
|
-
});
|
|
43598
|
-
continue;
|
|
43708
|
+
error: String(err)
|
|
43709
|
+
} });
|
|
43599
43710
|
}
|
|
43600
|
-
|
|
43601
|
-
location: from,
|
|
43602
|
-
relativePath
|
|
43603
|
-
});
|
|
43604
|
-
await this.deps.storage.write({
|
|
43605
|
-
location: job.toLocationId,
|
|
43606
|
-
relativePath,
|
|
43607
|
-
data: bytes
|
|
43608
|
-
});
|
|
43609
|
-
if ((await this.deps.storage.read({
|
|
43610
|
-
location: job.toLocationId,
|
|
43611
|
-
relativePath
|
|
43612
|
-
})).length !== bytes.length) throw new Error(`relocated media size mismatch for ${relativePath}`);
|
|
43613
|
-
await this.deps.store.set.mutate({
|
|
43614
|
-
collection: MEDIA_COLLECTION,
|
|
43615
|
-
key: row.id,
|
|
43616
|
-
value: {
|
|
43617
|
-
...data,
|
|
43618
|
-
locationId: job.toLocationId
|
|
43619
|
-
}
|
|
43620
|
-
});
|
|
43621
|
-
await this.deps.storage.delete({
|
|
43622
|
-
location: from,
|
|
43623
|
-
relativePath
|
|
43624
|
-
});
|
|
43625
|
-
job.filesMoved++;
|
|
43626
|
-
job.bytesMoved += bytes.length;
|
|
43627
|
-
await sleep(bytes.length / bytesPerMs);
|
|
43628
|
-
} catch (err) {
|
|
43629
|
-
failedRows++;
|
|
43630
|
-
this.deps.logger.debug("media relocate row failed", { meta: {
|
|
43631
|
-
key: row.id,
|
|
43632
|
-
error: String(err)
|
|
43633
|
-
} });
|
|
43634
|
-
}
|
|
43635
|
-
}
|
|
43636
|
-
const last = rows[rows.length - 1];
|
|
43637
|
-
const lastTs = Number(last.data["timestamp"] ?? cursor);
|
|
43638
|
-
if (lastTs === cursor) offsetAtCursor += rows.length;
|
|
43639
|
-
else {
|
|
43640
|
-
cursor = lastTs;
|
|
43641
|
-
offsetAtCursor = rows.filter((row) => Number(row.data["timestamp"]) === lastTs).length;
|
|
43642
|
-
}
|
|
43643
|
-
if (rows.length < PAGE_SIZE) break;
|
|
43644
|
-
}
|
|
43645
|
-
let retrainOffset = 0;
|
|
43646
|
-
for (;;) {
|
|
43647
|
-
if (job.cancelRequested) break;
|
|
43648
|
-
const rows = await this.deps.store.query.query({
|
|
43649
|
-
collection: RETRAIN_FRAMES_COLLECTION,
|
|
43650
|
-
filter: {
|
|
43651
|
-
orderBy: {
|
|
43652
|
-
field: "copiedAt",
|
|
43653
|
-
direction: "asc"
|
|
43654
|
-
},
|
|
43655
|
-
limit: PAGE_SIZE,
|
|
43656
|
-
offset: retrainOffset
|
|
43711
|
+
continue;
|
|
43657
43712
|
}
|
|
43658
|
-
});
|
|
43659
|
-
if (rows.length === 0) break;
|
|
43660
|
-
for (const row of rows) {
|
|
43661
|
-
if (job.cancelRequested) break;
|
|
43662
|
-
const data = row.data;
|
|
43663
43713
|
const from = mediaRowLocation(data);
|
|
43664
|
-
if (from === job.toLocationId) continue;
|
|
43665
43714
|
const relativePath = String(data["path"] ?? "");
|
|
43666
43715
|
if (relativePath.length === 0) {
|
|
43667
|
-
|
|
43716
|
+
pathlessRows++;
|
|
43668
43717
|
continue;
|
|
43669
43718
|
}
|
|
43670
43719
|
try {
|
|
43671
43720
|
if (await this.samePhysicalLocation(from, job.toLocationId)) {
|
|
43672
|
-
await this.
|
|
43673
|
-
collection: RETRAIN_FRAMES_COLLECTION,
|
|
43674
|
-
key: row.id,
|
|
43675
|
-
value: {
|
|
43676
|
-
...data,
|
|
43677
|
-
locationId: job.toLocationId
|
|
43678
|
-
}
|
|
43679
|
-
});
|
|
43721
|
+
await this.stamp(collection.name, row.id, data, job.toLocationId);
|
|
43680
43722
|
continue;
|
|
43681
43723
|
}
|
|
43682
43724
|
const bytes = await this.deps.storage.read({
|
|
@@ -43691,15 +43733,8 @@ var MediaRelocateEngine = class {
|
|
|
43691
43733
|
if ((await this.deps.storage.read({
|
|
43692
43734
|
location: job.toLocationId,
|
|
43693
43735
|
relativePath
|
|
43694
|
-
})).length !== bytes.length) throw new Error(`relocated
|
|
43695
|
-
await this.
|
|
43696
|
-
collection: RETRAIN_FRAMES_COLLECTION,
|
|
43697
|
-
key: row.id,
|
|
43698
|
-
value: {
|
|
43699
|
-
...data,
|
|
43700
|
-
locationId: job.toLocationId
|
|
43701
|
-
}
|
|
43702
|
-
});
|
|
43736
|
+
})).length !== bytes.length) throw new Error(`relocated media size mismatch for ${relativePath}`);
|
|
43737
|
+
await this.stamp(collection.name, row.id, data, job.toLocationId);
|
|
43703
43738
|
await this.deps.storage.delete({
|
|
43704
43739
|
location: from,
|
|
43705
43740
|
relativePath
|
|
@@ -43709,22 +43744,27 @@ var MediaRelocateEngine = class {
|
|
|
43709
43744
|
await sleep(bytes.length / bytesPerMs);
|
|
43710
43745
|
} catch (err) {
|
|
43711
43746
|
failedRows++;
|
|
43712
|
-
this.deps.logger.debug("
|
|
43747
|
+
this.deps.logger.debug("media relocate row failed", { meta: {
|
|
43748
|
+
collection: collection.name,
|
|
43713
43749
|
key: row.id,
|
|
43714
43750
|
error: String(err)
|
|
43715
43751
|
} });
|
|
43716
43752
|
}
|
|
43717
43753
|
}
|
|
43718
|
-
retrainOffset += rows.length;
|
|
43719
|
-
if (rows.length < PAGE_SIZE) break;
|
|
43720
43754
|
}
|
|
43721
|
-
if (
|
|
43755
|
+
if (pathlessRows > 0) this.deps.logger.warn("media relocate stepped over rows that name no blob path", { meta: {
|
|
43756
|
+
jobId: job.jobId,
|
|
43757
|
+
mode: job.mode,
|
|
43758
|
+
rows: pathlessRows
|
|
43759
|
+
} });
|
|
43760
|
+
if (failedRows > 0) throw new Error(`event-media ${job.mode} left ${failedRows} row(s) unmoved`);
|
|
43722
43761
|
job.state = job.cancelRequested ? "cancelled" : "done";
|
|
43723
43762
|
} catch (err) {
|
|
43724
43763
|
job.state = "failed";
|
|
43725
43764
|
job.error = err instanceof Error ? err.message : String(err);
|
|
43726
43765
|
this.deps.logger.warn("media relocate job failed", { meta: {
|
|
43727
43766
|
jobId: job.jobId,
|
|
43767
|
+
mode: job.mode,
|
|
43728
43768
|
error: job.error
|
|
43729
43769
|
} });
|
|
43730
43770
|
} finally {
|
|
@@ -43732,6 +43772,16 @@ var MediaRelocateEngine = class {
|
|
|
43732
43772
|
this.deps.onFinished?.(snapshot(job));
|
|
43733
43773
|
}
|
|
43734
43774
|
}
|
|
43775
|
+
async stamp(collection, key, data, locationId) {
|
|
43776
|
+
await this.deps.store.set.mutate({
|
|
43777
|
+
collection,
|
|
43778
|
+
key,
|
|
43779
|
+
value: {
|
|
43780
|
+
...data,
|
|
43781
|
+
locationId
|
|
43782
|
+
}
|
|
43783
|
+
});
|
|
43784
|
+
}
|
|
43735
43785
|
async samePhysicalLocation(from, to) {
|
|
43736
43786
|
if (from === to) return true;
|
|
43737
43787
|
try {
|
|
@@ -62299,6 +62349,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
62299
62349
|
logger: logger.child("RetrainFrames"),
|
|
62300
62350
|
now: () => Date.now(),
|
|
62301
62351
|
writeGate: this.mediaWriteGate,
|
|
62352
|
+
resolveEventMediaLocation: (deviceId) => this.resolveEventMediaWriteLocation(api, logger, deviceId),
|
|
62302
62353
|
readSource: async (mediaKey) => {
|
|
62303
62354
|
const file = await mediaStore.getByKey(mediaKey);
|
|
62304
62355
|
return file === null ? null : Buffer.from(file.base64, "base64");
|
|
@@ -68975,10 +69026,22 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
68975
69026
|
const jobId = this.mediaRelocate.start(input);
|
|
68976
69027
|
this.ctx.logger.info("pipeline-analytics: media relocate job enqueued", { meta: {
|
|
68977
69028
|
jobId,
|
|
69029
|
+
mode: input.mode ?? "move",
|
|
68978
69030
|
to: input.toLocationId
|
|
68979
69031
|
} });
|
|
68980
69032
|
return { jobId };
|
|
68981
69033
|
}
|
|
69034
|
+
/**
|
|
69035
|
+
* How many media / retrain rows still carry no `locationId`.
|
|
69036
|
+
*
|
|
69037
|
+
* Counted against the rows, never inferred from a seal job's terminal state:
|
|
69038
|
+
* the migration planner refuses a non-blocking `eventMedia` cutover on this
|
|
69039
|
+
* number, and a seal that failed some rows still finishes.
|
|
69040
|
+
*/
|
|
69041
|
+
async countUnstampedEventMedia() {
|
|
69042
|
+
if (!this.mediaRelocate) throw new Error("media relocation unavailable");
|
|
69043
|
+
return this.mediaRelocate.countUnstamped();
|
|
69044
|
+
}
|
|
68982
69045
|
/** Every relocate job this addon knows about, newest first (in RAM: the
|
|
68983
69046
|
* move is resumable, so a lost list costs nothing but the display). */
|
|
68984
69047
|
async listRelocateMediaJobs() {
|