@camstack/addon-pipeline 1.2.199 → 1.2.200
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/audio-analyzer/index.js +2 -2
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/{default-detection-model-CMe44Kid.js → default-detection-model-BsmxtyZx.js} +1 -1
- package/dist/{default-detection-model-O1uBusV2.mjs → default-detection-model-c1LY6thP.mjs} +1 -1
- package/dist/detection-pipeline/index.js +5 -5
- package/dist/detection-pipeline/index.mjs +4 -4
- package/dist/{dist-DmCrC2yx.js → dist-D2YTTkRf.js} +424 -426
- package/dist/{dist-B23u1tmN.mjs → dist-mEsrAAc3.mjs} +424 -426
- package/dist/{lazy-sharp-DBHsD2lh.js → lazy-sharp-B5bW34uC.js} +1 -1
- package/dist/motion-wasm/index.js +2 -2
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-D1MLn6oV.mjs → node-CUxep5tc.mjs} +1 -1
- package/dist/{node-DCSTbt6C.js → node-x3YokJkD.js} +1 -1
- package/dist/pipeline-runner/index.js +8 -7
- package/dist/pipeline-runner/index.mjs +7 -6
- package/dist/{process-memory-P24NTDb1.mjs → process-memory-BCqpTruN.mjs} +1 -1
- package/dist/{process-memory-9lvvhHym.js → process-memory-CFc4D9A2.js} +1 -1
- package/dist/recorder/index.js +169 -334
- package/dist/recorder/index.mjs +164 -329
- package/dist/restream-intent-B1Difcnq.js +313 -0
- package/dist/restream-intent-DWe2kInY.mjs +248 -0
- package/dist/{prebuffer-DmBYRQWg.mjs → retire-root-keys-Csb8z2f3.js} +12 -64
- package/dist/{prebuffer-DqdeLSsk.js → retire-root-keys-DZvlso2l.mjs} +1 -99
- package/dist/{segment-demux-js-Ck_F5bsO.js → segment-demux-js-DUZVURQd.js} +1 -1
- package/dist/{segment-demux-js-Bm0woPkn.mjs → segment-demux-js-xHtB450F.mjs} +1 -1
- package/dist/session-decode/decode-worker-child.js +25 -5
- package/dist/session-decode/decode-worker-child.mjs +24 -4
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-Sq93hI-g.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CLXImQ3R.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-NqqfpkYF.mjs +26 -0
- package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Dj_qoc1e.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-D62O5bRa.mjs} +1 -1
- package/dist/stream-broker/demux-worker-child.js +1 -1
- package/dist/stream-broker/demux-worker-child.mjs +1 -1
- package/dist/stream-broker/{hostInit-COt259tU.mjs → hostInit-D_cZQhBA.mjs} +3 -3
- package/dist/stream-broker/index.js +181 -48
- package/dist/stream-broker/index.mjs +172 -39
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/{worker-protocol-c1r1Yddg.js → worker-protocol-CRDaBlJl.js} +1 -1
- package/dist/{worker-protocol-BxpGZ0Dt.mjs → worker-protocol-oUamwZSn.mjs} +1 -1
- package/package.json +1 -1
- package/dist/restream-intent-B4BXZra7.mjs +0 -72
- package/dist/restream-intent-Cv9x3jmu.js +0 -89
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BcjLrxhF.mjs +0 -26
package/dist/recorder/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Bt as hydrateSchema, Ct as storageEvictableCapability, D as RecordingSignalStatusSchema, E as RecordingConfigSchema, Ft as BaseAddon, H as deriveBatteryPresence, Jt as sleep$1, Lt as DeviceFeature, Ot as errMsg, P as batteryCapability, R as customAction, Rt as DeviceType, T as RECORDING_EXPORT_MAX_READ_BYTES, Tt as stripRetiredBandPreBufferSec, V as defineCustomActions, W as deriveRecordingMode, Wt as nodePin, Xt as array, Y as evaluateSensorEdge, Yt as _enum, Zt as boolean, _t as recordingSignalCapability, an as string, at as migrateLegacyDeviceSignalConfig, d as DEFAULT_EVENTS_BAND_BUFFER_SEC, et as isFirstLevelMacroClass, g as ExportRecordSchema, gt as recordingExportCapability, ht as recordingCapability, i as BatteryStatusSchema, in as record, nn as object, qt as selectAssignedProfileSlots, rn as preprocess, sn as EventCategory, tn as number, tt as loadContributionCapability, x as OpsLogEntrySchema, xt as resolveRecordingProfiles } from "../dist-
|
|
2
|
-
import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-
|
|
1
|
+
import { Bt as hydrateSchema, Ct as storageEvictableCapability, D as RecordingSignalStatusSchema, E as RecordingConfigSchema, Ft as BaseAddon, H as deriveBatteryPresence, Jt as sleep$1, Lt as DeviceFeature, Ot as errMsg, P as batteryCapability, R as customAction, Rt as DeviceType, T as RECORDING_EXPORT_MAX_READ_BYTES, Tt as stripRetiredBandPreBufferSec, V as defineCustomActions, W as deriveRecordingMode, Wt as nodePin, Xt as array, Y as evaluateSensorEdge, Yt as _enum, Zt as boolean, _t as recordingSignalCapability, an as string, at as migrateLegacyDeviceSignalConfig, d as DEFAULT_EVENTS_BAND_BUFFER_SEC, et as isFirstLevelMacroClass, g as ExportRecordSchema, gt as recordingExportCapability, ht as recordingCapability, i as BatteryStatusSchema, in as record, nn as object, qt as selectAssignedProfileSlots, rn as preprocess, sn as EventCategory, tn as number, tt as loadContributionCapability, x as OpsLogEntrySchema, xt as resolveRecordingProfiles } from "../dist-mEsrAAc3.mjs";
|
|
2
|
+
import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-CUxep5tc.mjs";
|
|
3
3
|
import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
|
|
4
|
-
import { r as withRecordingIntent } from "../restream-intent-
|
|
4
|
+
import { c as PREBUFFER_MAX_MS, r as RECORDING_PRE_ROLL_REQUEST_MS, s as withRecordingIntent } from "../restream-intent-DWe2kInY.mjs";
|
|
5
5
|
import { n as startEventLoopStallMonitor } from "../event-loop-stall-monitor-DXLNBMkY.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DZvlso2l.mjs";
|
|
7
7
|
import { randomUUID } from "node:crypto";
|
|
8
8
|
import { createReadStream, promises, realpathSync } from "node:fs";
|
|
9
9
|
import path from "node:path";
|
|
@@ -7727,96 +7727,59 @@ function runFfmpeg(renderDeps, args, ext) {
|
|
|
7727
7727
|
});
|
|
7728
7728
|
});
|
|
7729
7729
|
}
|
|
7730
|
-
|
|
7731
|
-
//#region src/recorder/addon/roots.ts
|
|
7732
|
-
/**
|
|
7733
|
-
* Storage-root resolution + boot hydrate for the recorder addon.
|
|
7734
|
-
*
|
|
7735
|
-
* Recordings live in the managed `recordings` / `recordingsLow` storage
|
|
7736
|
-
* locations (operator-configurable, multi-disk-ready), resolved through the
|
|
7737
|
-
* `storage` cap. The v2 `RecordingIndex` is hydrated by walking each location
|
|
7738
|
-
* root's `<deviceId>/` subtree on disk — segment paths encode start/dur/bytes,
|
|
7739
|
-
* so the in-memory index can never drift from disk. (We walk the filesystem
|
|
7740
|
-
* directly instead of `storage.list` because that cap lists only a directory's
|
|
7741
|
-
* IMMEDIATE children, not the `<deviceId>/<profile>/Y/M/D/H/*.m4s` tree — using
|
|
7742
|
-
* it dropped the entire index to ~empty on every restart/rescan; the recorder
|
|
7743
|
-
* already does local fs I/O for stat/move on these same roots.)
|
|
7744
|
-
*/
|
|
7745
|
-
/** The two recordings location types the recorder addon writes/reads. */
|
|
7746
|
-
var PLACEMENT_LOCATION_TYPES = ["recordings", "recordingsLow"];
|
|
7747
|
-
var RECORDING_LOCATION_TYPES = new Set(PLACEMENT_LOCATION_TYPES);
|
|
7730
|
+
var RECORDING_LOCATION_TYPES = new Set(["recordings", "recordingsLow"]);
|
|
7748
7731
|
/**
|
|
7749
7732
|
* The recordings CLASS a profile belongs to. The one place this rule is
|
|
7750
|
-
* written: `resolvePlacement`, the planner's demand type and the
|
|
7751
|
-
* must agree on it, and three copies of `profile === 'low' ? … : …` is
|
|
7752
|
-
* stop agreeing.
|
|
7733
|
+
* written: `resolvePlacement`, the planner's demand type and the placement
|
|
7734
|
+
* census must agree on it, and three copies of `profile === 'low' ? … : …` is
|
|
7735
|
+
* how they stop agreeing.
|
|
7753
7736
|
*/
|
|
7754
7737
|
function placementTypeForProfile(profile) {
|
|
7755
7738
|
return profile === "low" ? "recordingsLow" : "recordings";
|
|
7756
7739
|
}
|
|
7757
|
-
/** The profile that routes to each class — the probe `authorityLocations` asks
|
|
7758
|
-
* `resolvePlacement` with, so the answer is derived from the same function the
|
|
7759
|
-
* unassigned path uses rather than from a second reading of `isDefault`. */
|
|
7760
|
-
var CLASS_PROBE_PROFILE = {
|
|
7761
|
-
recordings: "high",
|
|
7762
|
-
recordingsLow: "low"
|
|
7763
|
-
};
|
|
7764
|
-
/**
|
|
7765
|
-
* Where each recordings class writes when NOTHING has planned it — the location
|
|
7766
|
-
* `resolvePlacement` picks, which is the type default unless the default is
|
|
7767
|
-
* gone, read-only, or absent from the pool.
|
|
7768
|
-
*
|
|
7769
|
-
* This is the AUTHORITY a stored assignment is measured against (D298). A
|
|
7770
|
-
* repoint (`storage.setDefaultLocations`, an operator making another disk the
|
|
7771
|
-
* default, a default deleted) changes exactly this map, and a stored assignment
|
|
7772
|
-
* that survives that change is a plan that outlived the pool it was planned
|
|
7773
|
-
* for.
|
|
7774
|
-
*
|
|
7775
|
-
* A class with nothing writable is ABSENT from the map, not guessed at: `place`
|
|
7776
|
-
* already throws loudly on that path, and an authority nobody can compute must
|
|
7777
|
-
* invalidate nothing.
|
|
7778
|
-
*/
|
|
7779
|
-
function authorityLocations(pool) {
|
|
7780
|
-
const out = /* @__PURE__ */ new Map();
|
|
7781
|
-
for (const type of PLACEMENT_LOCATION_TYPES) try {
|
|
7782
|
-
out.set(type, resolvePlacement(pool, CLASS_PROBE_PROFILE[type]).id);
|
|
7783
|
-
} catch {}
|
|
7784
|
-
return out;
|
|
7785
|
-
}
|
|
7786
7740
|
/**
|
|
7787
7741
|
* Narrow a resolved location list to the ones placement may write to: the
|
|
7788
|
-
*
|
|
7789
|
-
* (`enabled === false`) is still READ, age-swept and drained — it is simply
|
|
7742
|
+
* enabled ones — ALL of them, together. A location the operator has not opted
|
|
7743
|
+
* in (`enabled === false`) is still READ, age-swept and drained — it is simply
|
|
7790
7744
|
* never a write target, exactly like a read-only one.
|
|
7791
7745
|
*
|
|
7792
|
-
* When no enabled location is writable
|
|
7793
|
-
*
|
|
7794
|
-
* only
|
|
7795
|
-
* is worse than one that writes
|
|
7746
|
+
* When no enabled location is writable EVERY location serves instead, loudly.
|
|
7747
|
+
* The orchestrator refuses to leave a recordings class without an enabled
|
|
7748
|
+
* location, so this is reachable only through a hand-edited store — and a
|
|
7749
|
+
* recorder that writes nowhere is worse than one that writes under protest.
|
|
7796
7750
|
*/
|
|
7797
7751
|
function placementPool(locations) {
|
|
7798
7752
|
const enabled = locations.filter((l) => l.enabled !== false);
|
|
7799
7753
|
if (enabled.some((l) => !l.readOnly)) return {
|
|
7800
7754
|
locations: enabled,
|
|
7801
|
-
|
|
7802
|
-
};
|
|
7803
|
-
const defaults = locations.filter((l) => l.isDefault === true);
|
|
7804
|
-
if (defaults.some((l) => !l.readOnly)) return {
|
|
7805
|
-
locations: defaults,
|
|
7806
|
-
fellBackToDefault: true
|
|
7755
|
+
fellBackToDisabled: false
|
|
7807
7756
|
};
|
|
7808
7757
|
return {
|
|
7809
7758
|
locations,
|
|
7810
|
-
|
|
7759
|
+
fellBackToDisabled: true
|
|
7811
7760
|
};
|
|
7812
7761
|
}
|
|
7813
7762
|
/**
|
|
7814
|
-
* Route a recording profile to
|
|
7815
|
-
*
|
|
7816
|
-
*
|
|
7817
|
-
*
|
|
7818
|
-
*
|
|
7819
|
-
*
|
|
7763
|
+
* Route a recording profile to a storage location, for a (camera, profile) the
|
|
7764
|
+
* PLANNER has not placed yet — the first attach of a new camera, before the
|
|
7765
|
+
* 30 s pass runs.
|
|
7766
|
+
*
|
|
7767
|
+
* The rule, in order:
|
|
7768
|
+
* 1. the location's declared `type` — `low` prefers `recordingsLow`, every
|
|
7769
|
+
* other profile prefers `recordings`; when the preferred type has only
|
|
7770
|
+
* read-only instances, any writable recordings-type location serves;
|
|
7771
|
+
* 2. among those, the one with the most MEASURED headroom
|
|
7772
|
+
* (`min(free − minFree floor, maxUsedGb − used)`) — the same signal the
|
|
7773
|
+
* planner balances on, so the bootstrap pick and the plan agree instead of
|
|
7774
|
+
* the plan having to undo the pick at the next tick;
|
|
7775
|
+
* 3. ties, and a pool where nothing was measured, fall to the
|
|
7776
|
+
* lexicographically first id — deterministic, never "whichever came back
|
|
7777
|
+
* first from the cap".
|
|
7778
|
+
*
|
|
7779
|
+
* There is no preferred instance any more (D383): every enabled location of a
|
|
7780
|
+
* class is a write target, and which one a camera gets is a capacity question,
|
|
7781
|
+
* not a flag. Throws when NO writable location exists — recording cannot start,
|
|
7782
|
+
* and that must be loud.
|
|
7820
7783
|
*/
|
|
7821
7784
|
function resolvePlacement(locations, profile) {
|
|
7822
7785
|
if (locations.length === 0) throw new Error("recorder controller: no recordings storage locations resolved");
|
|
@@ -7824,7 +7787,15 @@ function resolvePlacement(locations, profile) {
|
|
|
7824
7787
|
if (writable.length === 0) throw new Error("recorder controller: every recordings location is read-only — nowhere to write");
|
|
7825
7788
|
const preferType = profile === "low" ? "recordingsLow" : "recordings";
|
|
7826
7789
|
const preferred = writable.filter((l) => l.type === preferType);
|
|
7827
|
-
return preferred.
|
|
7790
|
+
return emptiestOf(preferred.length > 0 ? preferred : writable);
|
|
7791
|
+
}
|
|
7792
|
+
/** The candidate with the most measured headroom; ties (and an unmeasured
|
|
7793
|
+
* pool, where every headroom is absent) by lexicographic id. */
|
|
7794
|
+
function emptiestOf(candidates) {
|
|
7795
|
+
return candidates.toSorted((a, b) => {
|
|
7796
|
+
const byHeadroom = (b.headroomBytes ?? 0) - (a.headroomBytes ?? 0);
|
|
7797
|
+
return byHeadroom !== 0 ? byHeadroom : a.id.localeCompare(b.id);
|
|
7798
|
+
})[0];
|
|
7828
7799
|
}
|
|
7829
7800
|
/**
|
|
7830
7801
|
* Attribute a discovered segment on an aliased physical root by its profile,
|
|
@@ -7874,7 +7845,6 @@ async function resolveRecordingsLocations(api, logger) {
|
|
|
7874
7845
|
relativePath: ""
|
|
7875
7846
|
}),
|
|
7876
7847
|
readOnly: l.config["readOnly"] === true,
|
|
7877
|
-
isDefault: l.isDefault,
|
|
7878
7848
|
...l.enabled === false ? { enabled: false } : {},
|
|
7879
7849
|
...numericConfig(l.config, "minFreePercent", "minFreePercent"),
|
|
7880
7850
|
...numericConfig(l.config, "maxUsedGb", "maxUsedGb")
|
|
@@ -10432,47 +10402,17 @@ Object.freeze({
|
|
|
10432
10402
|
},
|
|
10433
10403
|
"setDeviceConfig": { "config": { "__nested__": { "bands": [] } } }
|
|
10434
10404
|
},
|
|
10435
|
-
"storage": { "upsertLocation": {
|
|
10436
|
-
"isDefault": false,
|
|
10437
|
-
"isSystem": false
|
|
10438
|
-
} },
|
|
10405
|
+
"storage": { "upsertLocation": { "isSystem": false } },
|
|
10439
10406
|
"storage-provider": {
|
|
10440
|
-
"beginDownload": { "location": { "__nested__": {
|
|
10441
|
-
|
|
10442
|
-
|
|
10443
|
-
} } },
|
|
10444
|
-
"
|
|
10445
|
-
|
|
10446
|
-
|
|
10447
|
-
} } },
|
|
10448
|
-
"
|
|
10449
|
-
"isDefault": false,
|
|
10450
|
-
"isSystem": false
|
|
10451
|
-
} } },
|
|
10452
|
-
"exists": { "location": { "__nested__": {
|
|
10453
|
-
"isDefault": false,
|
|
10454
|
-
"isSystem": false
|
|
10455
|
-
} } },
|
|
10456
|
-
"getAvailableSpace": { "location": { "__nested__": {
|
|
10457
|
-
"isDefault": false,
|
|
10458
|
-
"isSystem": false
|
|
10459
|
-
} } },
|
|
10460
|
-
"list": { "location": { "__nested__": {
|
|
10461
|
-
"isDefault": false,
|
|
10462
|
-
"isSystem": false
|
|
10463
|
-
} } },
|
|
10464
|
-
"read": { "location": { "__nested__": {
|
|
10465
|
-
"isDefault": false,
|
|
10466
|
-
"isSystem": false
|
|
10467
|
-
} } },
|
|
10468
|
-
"resolve": { "location": { "__nested__": {
|
|
10469
|
-
"isDefault": false,
|
|
10470
|
-
"isSystem": false
|
|
10471
|
-
} } },
|
|
10472
|
-
"write": { "location": { "__nested__": {
|
|
10473
|
-
"isDefault": false,
|
|
10474
|
-
"isSystem": false
|
|
10475
|
-
} } }
|
|
10407
|
+
"beginDownload": { "location": { "__nested__": { "isSystem": false } } },
|
|
10408
|
+
"beginUpload": { "location": { "__nested__": { "isSystem": false } } },
|
|
10409
|
+
"delete": { "location": { "__nested__": { "isSystem": false } } },
|
|
10410
|
+
"exists": { "location": { "__nested__": { "isSystem": false } } },
|
|
10411
|
+
"getAvailableSpace": { "location": { "__nested__": { "isSystem": false } } },
|
|
10412
|
+
"list": { "location": { "__nested__": { "isSystem": false } } },
|
|
10413
|
+
"read": { "location": { "__nested__": { "isSystem": false } } },
|
|
10414
|
+
"resolve": { "location": { "__nested__": { "isSystem": false } } },
|
|
10415
|
+
"write": { "location": { "__nested__": { "isSystem": false } } }
|
|
10476
10416
|
},
|
|
10477
10417
|
"stream-broker": {
|
|
10478
10418
|
"produceEventMedia": {
|
|
@@ -11490,19 +11430,16 @@ var RecordingPlacementBlobSchema = object({
|
|
|
11490
11430
|
/** locationId → the volume id last verified at that location's root. */
|
|
11491
11431
|
volumeIds: record(string(), string()).default({}),
|
|
11492
11432
|
/**
|
|
11493
|
-
*
|
|
11494
|
-
*
|
|
11495
|
-
*
|
|
11496
|
-
*
|
|
11497
|
-
* as a repoint: shipping this field must not invalidate assignments that were
|
|
11498
|
-
* made under an authority nothing wrote down. The first pass stamps it and
|
|
11499
|
-
* invalidates nothing; every later change of it is a repoint.
|
|
11433
|
+
* DEAD KEY (D383) — the class authority a pass planned against, back when a
|
|
11434
|
+
* class had a default distinct from its pool. Declared so an existing blob
|
|
11435
|
+
* round-trips unchanged and so its removal is a decision on the record rather
|
|
11436
|
+
* than a silent strip. Never read, never written.
|
|
11500
11437
|
*/
|
|
11501
11438
|
defaultLocations: record(string(), string()).default({}),
|
|
11502
11439
|
/** When the blob was last written (operator-facing debugging only). */
|
|
11503
11440
|
updatedAt: number().optional()
|
|
11504
11441
|
});
|
|
11505
|
-
/** Cold-start value: nothing assigned, nothing claimed
|
|
11442
|
+
/** Cold-start value: nothing assigned, nothing claimed. */
|
|
11506
11443
|
var EMPTY_PLACEMENT = {
|
|
11507
11444
|
assignments: {},
|
|
11508
11445
|
pins: {},
|
|
@@ -11551,31 +11488,27 @@ async function replaceAssignments(state, base, assignments) {
|
|
|
11551
11488
|
});
|
|
11552
11489
|
}
|
|
11553
11490
|
/**
|
|
11554
|
-
* The assignment record a pass leaves behind: the stored one
|
|
11555
|
-
*
|
|
11556
|
-
*
|
|
11491
|
+
* The assignment record a pass leaves behind: the stored one with the plan
|
|
11492
|
+
* merged over it. **Nothing is ever removed** — a pass that plans a subset
|
|
11493
|
+
* cannot delete a key it did not mention.
|
|
11557
11494
|
*
|
|
11558
|
-
* PURE, and shared by the write and the
|
|
11495
|
+
* PURE, and shared by the write and the placement census — a report computed
|
|
11559
11496
|
* from a second reading of the same rule is a report that can describe a record
|
|
11560
11497
|
* the write did not produce.
|
|
11561
11498
|
*/
|
|
11562
11499
|
function applyPlacementPass(base, pass) {
|
|
11563
11500
|
const next = new Map(Object.entries(base));
|
|
11564
|
-
for (const [key, locationId] of pass.repointed) next.set(key, locationId);
|
|
11565
11501
|
for (const [key, locationId] of pass.planned) next.set(key, locationId);
|
|
11566
11502
|
return next;
|
|
11567
11503
|
}
|
|
11568
|
-
/** Persist one work-scoped pass:
|
|
11569
|
-
*
|
|
11504
|
+
/** Persist one work-scoped pass: merge its plan over the stored record. One
|
|
11505
|
+
* write. */
|
|
11570
11506
|
async function writePlacementPass(state, base, pass) {
|
|
11571
11507
|
const assignments = {};
|
|
11572
11508
|
for (const [key, locationId] of applyPlacementPass(base.assignments, pass)) assignments[key] = locationId;
|
|
11573
|
-
const defaultLocations = {};
|
|
11574
|
-
for (const [type, locationId] of pass.defaultLocations) defaultLocations[type] = locationId;
|
|
11575
11509
|
await state.set({
|
|
11576
11510
|
...base,
|
|
11577
11511
|
assignments,
|
|
11578
|
-
defaultLocations,
|
|
11579
11512
|
updatedAt: Date.now()
|
|
11580
11513
|
});
|
|
11581
11514
|
}
|
|
@@ -11824,14 +11757,6 @@ var NO_PLAN = {
|
|
|
11824
11757
|
assignments: /* @__PURE__ */ new Map(),
|
|
11825
11758
|
changes: []
|
|
11826
11759
|
};
|
|
11827
|
-
/** Is the authority already stamped, exactly? A pass that would change the
|
|
11828
|
-
* stamp always writes, even when it moved nothing: an unstamped authority is
|
|
11829
|
-
* an undetectable repoint. */
|
|
11830
|
-
function sameStamp(stamped, authority) {
|
|
11831
|
-
if (stamped.size !== authority.size) return false;
|
|
11832
|
-
for (const [type, locationId] of authority) if (stamped.get(type) !== locationId) return false;
|
|
11833
|
-
return true;
|
|
11834
|
-
}
|
|
11835
11760
|
/** Operator pins win over the planner for high/mid of the pinned camera. */
|
|
11836
11761
|
function overlayPins(planned, pins) {
|
|
11837
11762
|
const next = new Map(planned);
|
|
@@ -11859,14 +11784,11 @@ var PlacementService = class {
|
|
|
11859
11784
|
/** The fair tie-break the PURE planner defers to (D37). Advisory state, and
|
|
11860
11785
|
* consulted only for a tie the capacity signal cannot separate. */
|
|
11861
11786
|
rotation = new PlacementRotation();
|
|
11862
|
-
/** The
|
|
11863
|
-
* announced yet
|
|
11864
|
-
|
|
11865
|
-
/** When the
|
|
11866
|
-
|
|
11867
|
-
/** Repoint episodes already announced (`type:from->to`). Cleared the moment a
|
|
11868
|
-
* pass sees no repoint at all — the next one is a NEW episode. */
|
|
11869
|
-
repointAnnounced = /* @__PURE__ */ new Set();
|
|
11787
|
+
/** The census last announced, as a stable signature. `null` ⇒ nothing
|
|
11788
|
+
* announced yet. */
|
|
11789
|
+
censusSignature = null;
|
|
11790
|
+
/** When the census was last announced. */
|
|
11791
|
+
censusReportedAt = null;
|
|
11870
11792
|
constructor(deps) {
|
|
11871
11793
|
this.deps = deps;
|
|
11872
11794
|
}
|
|
@@ -11901,15 +11823,14 @@ var PlacementService = class {
|
|
|
11901
11823
|
async place(deviceId, profile) {
|
|
11902
11824
|
const pool = this.pool();
|
|
11903
11825
|
this.reprobeAfterBackoff(pool);
|
|
11904
|
-
const
|
|
11905
|
-
const resolved = await this.assignedLocation(deviceId, profile, pool, authority) ?? resolvePlacement(pool, profile);
|
|
11826
|
+
const resolved = await this.assignedLocation(deviceId, profile, pool) ?? resolvePlacement(await this.withHeadroom(pool), profile);
|
|
11906
11827
|
const usable = await this.ensureUsable(resolved, pool, profile, deviceId);
|
|
11907
11828
|
if (this.everyLocationUnusable(pool)) {
|
|
11908
11829
|
this.reportEveryLocationUnusable(deviceId, profile, resolved, pool);
|
|
11909
11830
|
return resolved;
|
|
11910
11831
|
}
|
|
11911
11832
|
this.allUnusableReported.delete(deviceId);
|
|
11912
|
-
await this.recordDecision(deviceId, profile, usable, resolved.id === usable.id
|
|
11833
|
+
await this.recordDecision(deviceId, profile, usable, resolved.id === usable.id);
|
|
11913
11834
|
return usable;
|
|
11914
11835
|
}
|
|
11915
11836
|
/**
|
|
@@ -11993,35 +11914,30 @@ var PlacementService = class {
|
|
|
11993
11914
|
* to it, so an unknown flag is the only thing that counts as opted in.
|
|
11994
11915
|
*/
|
|
11995
11916
|
pool() {
|
|
11996
|
-
const { locations,
|
|
11997
|
-
if (
|
|
11917
|
+
const { locations, fellBackToDisabled } = placementPool(this.deps.locations());
|
|
11918
|
+
if (fellBackToDisabled) this.deps.logger.warn("recorder placement: NO recordings location is enabled for writing — writing to a DISABLED location under protest. Enable one in Storage.", { meta: { locationIds: locations.map((l) => l.id) } });
|
|
11998
11919
|
return locations;
|
|
11999
11920
|
}
|
|
12000
11921
|
/**
|
|
12001
11922
|
* The stored assignment for one (camera, profile), when it is still valid.
|
|
12002
11923
|
*
|
|
12003
|
-
*
|
|
12004
|
-
*
|
|
12005
|
-
* 1. the location is absent from the current pool (deleted, opted out);
|
|
12006
|
-
* 2. the location is read-only (an operator drain);
|
|
12007
|
-
* 3. **the class was REPOINTED under it** — the authority stamped in the
|
|
12008
|
-
* blob is not the authority now, and the assignment names neither.
|
|
11924
|
+
* Two things invalidate it, and under the operator's model (D383) there is
|
|
11925
|
+
* nothing else that can:
|
|
12009
11926
|
*
|
|
12010
|
-
*
|
|
12011
|
-
*
|
|
12012
|
-
*
|
|
12013
|
-
*
|
|
12014
|
-
*
|
|
12015
|
-
* returning the new default changed nothing — it is the fallback, not the
|
|
12016
|
-
* authority. On 2026-08-29 that left two of thirteen cameras writing to the
|
|
12017
|
-
* disk the fleet was migrating off, silently (D298).
|
|
11927
|
+
* 1. the location is absent from the current POOL — deleted, or the
|
|
11928
|
+
* operator disabled it. The pool is the single write authority, so an
|
|
11929
|
+
* assignment naming a location outside it is not a divergence to report,
|
|
11930
|
+
* it is unrepresentable;
|
|
11931
|
+
* 2. the location is read-only (an operator drain).
|
|
12018
11932
|
*
|
|
12019
|
-
*
|
|
12020
|
-
*
|
|
12021
|
-
*
|
|
12022
|
-
*
|
|
11933
|
+
* There used to be a third — "the class was REPOINTED under it", measured
|
|
11934
|
+
* against the class default stamped in the blob (D301). It existed only
|
|
11935
|
+
* because the default was a SECOND write authority layered over the pool and
|
|
11936
|
+
* the two could disagree. With the default deleted the disagreement cannot be
|
|
11937
|
+
* expressed: a location leaving the pool already invalidates every assignment
|
|
11938
|
+
* naming it, and the planner then moves those keys with `reason: 'gone'`.
|
|
12023
11939
|
*/
|
|
12024
|
-
async assignedLocation(deviceId, profile, pool
|
|
11940
|
+
async assignedLocation(deviceId, profile, pool) {
|
|
12025
11941
|
let blob;
|
|
12026
11942
|
try {
|
|
12027
11943
|
blob = await readPlacementState(this.deps.state);
|
|
@@ -12043,23 +11959,28 @@ var PlacementService = class {
|
|
|
12043
11959
|
}
|
|
12044
11960
|
if (assignedId === void 0) return null;
|
|
12045
11961
|
const location = pool.find((l) => l.id === assignedId && !l.readOnly) ?? null;
|
|
12046
|
-
if (location === null)
|
|
12047
|
-
const type = placementTypeForProfile(profile);
|
|
12048
|
-
const target = authority.get(type);
|
|
12049
|
-
const stamped = blob.defaultLocations[type];
|
|
12050
|
-
if (target === void 0 || stamped === void 0) return location;
|
|
12051
|
-
if (stamped === target || location.id === target) return location;
|
|
12052
|
-
this.deps.logger.warn("recorder placement: stored assignment INVALIDATED — this recordings class was repointed and the assignment still names the location it was repointed away from", {
|
|
11962
|
+
if (location === null) this.deps.logger.info("recorder placement: stored assignment INVALIDATED — its location is not a write target any more (disabled, read-only, or gone). Resolving fresh from the pool.", {
|
|
12053
11963
|
tags: { deviceId },
|
|
12054
11964
|
meta: {
|
|
12055
11965
|
profile,
|
|
12056
|
-
assignedLocationId:
|
|
12057
|
-
|
|
12058
|
-
repointedTo: target,
|
|
12059
|
-
locationType: type
|
|
11966
|
+
assignedLocationId: assignedId,
|
|
11967
|
+
poolLocationIds: pool.filter((l) => !l.readOnly).map((l) => l.id)
|
|
12060
11968
|
}
|
|
12061
11969
|
});
|
|
12062
|
-
return
|
|
11970
|
+
return location;
|
|
11971
|
+
}
|
|
11972
|
+
/** The pool, with each member's measured headroom attached. One capacity read
|
|
11973
|
+
* per location; used only on the bootstrap path, never per segment. */
|
|
11974
|
+
async withHeadroom(pool) {
|
|
11975
|
+
const candidates = await this.candidatesFor(pool);
|
|
11976
|
+
const byId = new Map(candidates.map((c) => [c.locationId, c.headroomBytes]));
|
|
11977
|
+
return pool.map((location) => {
|
|
11978
|
+
const headroomBytes = byId.get(location.id);
|
|
11979
|
+
return headroomBytes === void 0 ? location : {
|
|
11980
|
+
...location,
|
|
11981
|
+
headroomBytes
|
|
11982
|
+
};
|
|
11983
|
+
});
|
|
12063
11984
|
}
|
|
12064
11985
|
/**
|
|
12065
11986
|
* Re-plan the (camera, profile) pairs in `work` over the current locations.
|
|
@@ -12081,7 +12002,6 @@ var PlacementService = class {
|
|
|
12081
12002
|
*/
|
|
12082
12003
|
async recompute(work) {
|
|
12083
12004
|
const pool = this.pool();
|
|
12084
|
-
const authority = authorityLocations(pool);
|
|
12085
12005
|
const candidates = await this.candidatesFor(pool);
|
|
12086
12006
|
const demands = work.map((item) => ({
|
|
12087
12007
|
key: assignmentKey(item.deviceId, item.profile),
|
|
@@ -12098,17 +12018,7 @@ var PlacementService = class {
|
|
|
12098
12018
|
} });
|
|
12099
12019
|
return NO_PLAN;
|
|
12100
12020
|
}
|
|
12101
|
-
const
|
|
12102
|
-
const repoints = this.detectRepoints(blob.assignments, stamped, authority, blob.pins);
|
|
12103
|
-
if (repoints.length === 0) this.repointAnnounced.clear();
|
|
12104
|
-
for (const repoint of repoints) this.announceRepoint(repoint);
|
|
12105
|
-
const repointed = /* @__PURE__ */ new Map();
|
|
12106
|
-
for (const repoint of repoints) for (const key of repoint.keys) repointed.set(key, repoint.to);
|
|
12107
|
-
const current = applyPlacementPass(blob.assignments, {
|
|
12108
|
-
repointed,
|
|
12109
|
-
planned: /* @__PURE__ */ new Map(),
|
|
12110
|
-
defaultLocations: authority
|
|
12111
|
-
});
|
|
12021
|
+
const current = new Map(Object.entries(blob.assignments));
|
|
12112
12022
|
const evacuate = new Set(candidates.filter((c) => c.headroomBytes <= 0).map((c) => c.locationId));
|
|
12113
12023
|
for (const location of pool) if (location.readOnly) evacuate.add(location.id);
|
|
12114
12024
|
const plan = planPlacement({
|
|
@@ -12118,13 +12028,9 @@ var PlacementService = class {
|
|
|
12118
12028
|
evacuate,
|
|
12119
12029
|
breakTie: (tied) => this.rotation.next(tied)
|
|
12120
12030
|
});
|
|
12121
|
-
const pass = {
|
|
12122
|
-
|
|
12123
|
-
|
|
12124
|
-
defaultLocations: authority
|
|
12125
|
-
};
|
|
12126
|
-
this.reportDivergence(applyPlacementPass(blob.assignments, pass), authority);
|
|
12127
|
-
if (plan.changes.length === 0 && repointed.size === 0 && sameStamp(stamped, authority)) return plan;
|
|
12031
|
+
const pass = { planned: overlayPins(plan.assignments, blob.pins) };
|
|
12032
|
+
this.reportCensus(applyPlacementPass(blob.assignments, pass), pool);
|
|
12033
|
+
if (plan.changes.length === 0) return plan;
|
|
12128
12034
|
for (const change of plan.changes) {
|
|
12129
12035
|
const deviceId = Number(change.key.split(":")[0]);
|
|
12130
12036
|
this.deps.logger.info("recorder placement: plan moves a profile to another location", {
|
|
@@ -12148,129 +12054,60 @@ var PlacementService = class {
|
|
|
12148
12054
|
return plan;
|
|
12149
12055
|
}
|
|
12150
12056
|
/**
|
|
12151
|
-
*
|
|
12152
|
-
*
|
|
12153
|
-
*
|
|
12154
|
-
* PURE. A class is repointed when the blob stamped an authority for it and
|
|
12155
|
-
* the authority now is a different location. A class with NO stamp is not a
|
|
12156
|
-
* repoint — it is the first pass after this shipped, and inferring a repoint
|
|
12157
|
-
* from an absent stamp would throw away every deliberate assignment on the
|
|
12158
|
-
* first tick after a deploy.
|
|
12159
|
-
*/
|
|
12160
|
-
detectRepoints(stored, stamped, authority, pins) {
|
|
12161
|
-
const out = [];
|
|
12162
|
-
for (const [type, to] of authority) {
|
|
12163
|
-
const from = stamped.get(type);
|
|
12164
|
-
if (from === void 0 || from === to) continue;
|
|
12165
|
-
const keys = [];
|
|
12166
|
-
for (const [key, locationId] of Object.entries(stored)) {
|
|
12167
|
-
const [devicePart, profilePart] = key.split(":");
|
|
12168
|
-
if (placementTypeForProfile(profilePart ?? "") !== type) continue;
|
|
12169
|
-
if (locationId === to) continue;
|
|
12170
|
-
if (type === "recordings" && devicePart !== void 0 && pins[devicePart] !== void 0) continue;
|
|
12171
|
-
keys.push(key);
|
|
12172
|
-
}
|
|
12173
|
-
out.push({
|
|
12174
|
-
type,
|
|
12175
|
-
from,
|
|
12176
|
-
to,
|
|
12177
|
-
keys
|
|
12178
|
-
});
|
|
12179
|
-
}
|
|
12180
|
-
return out;
|
|
12181
|
-
}
|
|
12182
|
-
/**
|
|
12183
|
-
* One line PER CAMERA (a repoint is asked per-camera: "did 3839 move?"), plus
|
|
12184
|
-
* one audit row per class. A repoint is a rare operator event, so the volume
|
|
12185
|
-
* is one burst of fleet-size, not a stream.
|
|
12057
|
+
* WHERE THE FLEET WRITES — one line per (class, location), always with its
|
|
12058
|
+
* denominator (D277).
|
|
12186
12059
|
*
|
|
12187
|
-
*
|
|
12188
|
-
*
|
|
12189
|
-
*
|
|
12190
|
-
*
|
|
12191
|
-
|
|
12192
|
-
|
|
12193
|
-
|
|
12194
|
-
if (this.repointAnnounced.has(episode)) return;
|
|
12195
|
-
this.repointAnnounced.add(episode);
|
|
12196
|
-
for (const key of repoint.keys) {
|
|
12197
|
-
const deviceId = Number(key.split(":")[0]);
|
|
12198
|
-
this.deps.logger.warn("recorder placement: recordings class REPOINTED — moving this assignment onto the class`s new authority location", {
|
|
12199
|
-
...Number.isFinite(deviceId) ? { tags: { deviceId } } : {},
|
|
12200
|
-
meta: {
|
|
12201
|
-
key,
|
|
12202
|
-
locationType: repoint.type,
|
|
12203
|
-
repointedFrom: repoint.from,
|
|
12204
|
-
repointedTo: repoint.to
|
|
12205
|
-
}
|
|
12206
|
-
});
|
|
12207
|
-
}
|
|
12208
|
-
this.deps.opsLog.append({
|
|
12209
|
-
op: "relocate",
|
|
12210
|
-
reason: "operator",
|
|
12211
|
-
deviceId: null,
|
|
12212
|
-
itemsAffected: repoint.keys.length,
|
|
12213
|
-
bytesReclaimed: 0,
|
|
12214
|
-
actor: "system",
|
|
12215
|
-
detail: `placement: ${repoint.type} repointed ${repoint.from} → ${repoint.to}; ${repoint.keys.length.toString()} assignment(s) moved (${repoint.keys.join(", ")})`
|
|
12216
|
-
});
|
|
12217
|
-
}
|
|
12218
|
-
/**
|
|
12219
|
-
* Say which (camera, profile) the plan writes somewhere other than its class
|
|
12220
|
-
* authority — per camera, WITH its denominator (D277).
|
|
12221
|
-
*
|
|
12222
|
-
* This is not an error path. The planner spreads a class across its pool on
|
|
12223
|
-
* purpose, and a location with headroom is a legitimate target. What was
|
|
12224
|
-
* wrong is that a deliberate spread and a leftover from a repoint nobody
|
|
12225
|
-
* noticed produced the *same* absence of output: `recordDecision` audits only
|
|
12226
|
-
* a CHANGE, so a camera that keeps using its old assignment writes no row and
|
|
12227
|
-
* no line, forever. An operator read "migration done" while two cameras were
|
|
12228
|
-
* still filling the disk they were migrating off.
|
|
12060
|
+
* This replaced the class-authority divergence report (D301) when the default
|
|
12061
|
+
* was deleted: with every enabled location of a class a legitimate target,
|
|
12062
|
+
* "this camera does not write to THE location" is not a fact any more, while
|
|
12063
|
+
* "recordings: 9 of 13 on recordings:default, 4 on recordings:hgst" is
|
|
12064
|
+
* exactly the question an operator asks after enabling a second disk. A pool
|
|
12065
|
+
* member holding ZERO assignments is listed too — an enabled disk nothing
|
|
12066
|
+
* lands on is the failure this whole model exists to make visible.
|
|
12229
12067
|
*
|
|
12230
|
-
* Fires on a CHANGE of the
|
|
12231
|
-
* {@link
|
|
12232
|
-
* until it stops being all-aligned, and again when it recovers.
|
|
12068
|
+
* Fires on a CHANGE of the distribution, and otherwise at most once per
|
|
12069
|
+
* {@link PLACEMENT_CENSUS_REPORT_MS}.
|
|
12233
12070
|
*/
|
|
12234
|
-
|
|
12235
|
-
const
|
|
12071
|
+
reportCensus(assignments, pool) {
|
|
12072
|
+
const counts = /* @__PURE__ */ new Map();
|
|
12073
|
+
const perClass = /* @__PURE__ */ new Map();
|
|
12074
|
+
for (const location of pool) if (!location.readOnly) counts.set(location.id, 0);
|
|
12236
12075
|
for (const [key, locationId] of assignments) {
|
|
12237
|
-
|
|
12238
|
-
const
|
|
12239
|
-
|
|
12240
|
-
divergent.push({
|
|
12241
|
-
key,
|
|
12242
|
-
deviceId: Number(key.split(":")[0]),
|
|
12243
|
-
profile,
|
|
12244
|
-
locationId,
|
|
12245
|
-
authorityLocationId
|
|
12246
|
-
});
|
|
12076
|
+
counts.set(locationId, (counts.get(locationId) ?? 0) + 1);
|
|
12077
|
+
const type = placementTypeForProfile(key.split(":")[1] ?? "");
|
|
12078
|
+
perClass.set(type, (perClass.get(type) ?? 0) + 1);
|
|
12247
12079
|
}
|
|
12248
|
-
const
|
|
12249
|
-
const
|
|
12080
|
+
const typeOf = new Map(pool.map((location) => [location.id, location.type]));
|
|
12081
|
+
const lines = [...counts.entries()].toSorted(([a], [b]) => a.localeCompare(b)).map(([locationId, count]) => {
|
|
12082
|
+
const type = typeOf.get(locationId) ?? locationId.split(":")[0] ?? locationId;
|
|
12083
|
+
return {
|
|
12084
|
+
locationId,
|
|
12085
|
+
type,
|
|
12086
|
+
assignments: count,
|
|
12087
|
+
ofClass: perClass.get(type) ?? 0
|
|
12088
|
+
};
|
|
12089
|
+
});
|
|
12090
|
+
const signature = lines.map((line) => `${line.locationId}=${String(line.assignments)}`).join(",");
|
|
12250
12091
|
const now = this.now;
|
|
12251
|
-
const due = this.
|
|
12252
|
-
if (signature ===
|
|
12253
|
-
this.
|
|
12254
|
-
this.
|
|
12255
|
-
if (
|
|
12256
|
-
|
|
12257
|
-
|
|
12092
|
+
const due = this.censusReportedAt === null || now - this.censusReportedAt >= 36e5;
|
|
12093
|
+
if (signature === this.censusSignature && !due) return;
|
|
12094
|
+
this.censusSignature = signature;
|
|
12095
|
+
this.censusReportedAt = now;
|
|
12096
|
+
if (lines.length === 0) return;
|
|
12097
|
+
this.deps.logger.info("recorder placement: census — which locations the fleet writes to", { meta: { locations: lines.map((line) => ({
|
|
12098
|
+
locationId: line.locationId,
|
|
12099
|
+
type: line.type,
|
|
12100
|
+
assignments: line.assignments,
|
|
12101
|
+
ofClass: line.ofClass
|
|
12102
|
+
})) } });
|
|
12103
|
+
for (const line of lines) {
|
|
12104
|
+
if (line.assignments > 0) continue;
|
|
12105
|
+
this.deps.logger.warn("recorder placement: this location is ENABLED but holds no assignment — nothing writes to it", { meta: {
|
|
12106
|
+
locationId: line.locationId,
|
|
12107
|
+
type: line.type,
|
|
12108
|
+
ofClass: line.ofClass
|
|
12109
|
+
} });
|
|
12258
12110
|
}
|
|
12259
|
-
this.deps.logger.warn("recorder placement: assignments write somewhere other than their class authority location", { meta: {
|
|
12260
|
-
divergent: divergent.length,
|
|
12261
|
-
assignments: assignments.size,
|
|
12262
|
-
authority: Object.fromEntries(authority)
|
|
12263
|
-
} });
|
|
12264
|
-
for (const entry of divergent) this.deps.logger.warn("recorder placement: this profile does NOT write to its class authority location", {
|
|
12265
|
-
...Number.isFinite(entry.deviceId) ? { tags: { deviceId: entry.deviceId } } : {},
|
|
12266
|
-
meta: {
|
|
12267
|
-
profile: entry.profile,
|
|
12268
|
-
locationId: entry.locationId,
|
|
12269
|
-
authorityLocationId: entry.authorityLocationId,
|
|
12270
|
-
divergent: divergent.length,
|
|
12271
|
-
assignments: assignments.size
|
|
12272
|
-
}
|
|
12273
|
-
});
|
|
12274
12111
|
}
|
|
12275
12112
|
/** Bound one addon-store round trip. See {@link PLACEMENT_STORE_TIMEOUT_MS}. */
|
|
12276
12113
|
bounded(promise, label) {
|
|
@@ -12451,13 +12288,13 @@ var PlacementService = class {
|
|
|
12451
12288
|
* Persist the assignment and audit it — but only when it actually changed.
|
|
12452
12289
|
* The steady state (every attach after the first) writes nothing.
|
|
12453
12290
|
*
|
|
12454
|
-
* The per-attach line carries
|
|
12455
|
-
*
|
|
12456
|
-
*
|
|
12457
|
-
*
|
|
12458
|
-
*
|
|
12291
|
+
* The per-attach line carries the camera's location and the one it replaced,
|
|
12292
|
+
* tagged with `deviceId`, so "where does 3839 write, and did it move?" is
|
|
12293
|
+
* answerable per camera. Which locations the FLEET is spread over is the
|
|
12294
|
+
* census's job (D383) — there is no class authority for a single line to be
|
|
12295
|
+
* measured against any more.
|
|
12459
12296
|
*/
|
|
12460
|
-
async recordDecision(deviceId, profile, location, verbatim
|
|
12297
|
+
async recordDecision(deviceId, profile, location, verbatim) {
|
|
12461
12298
|
const key = assignmentKey(deviceId, profile);
|
|
12462
12299
|
let previous;
|
|
12463
12300
|
try {
|
|
@@ -12478,8 +12315,6 @@ var PlacementService = class {
|
|
|
12478
12315
|
locationId: location.id,
|
|
12479
12316
|
root: location.root,
|
|
12480
12317
|
previousLocationId: previous ?? null,
|
|
12481
|
-
authorityLocationId,
|
|
12482
|
-
divergent: authorityLocationId !== null && authorityLocationId !== location.id,
|
|
12483
12318
|
reason: verbatim ? "resolved" : "failover"
|
|
12484
12319
|
}
|
|
12485
12320
|
});
|
|
@@ -15377,7 +15212,7 @@ var RecordingController = class {
|
|
|
15377
15212
|
tag: `recorder:${deviceId}/${profile}`
|
|
15378
15213
|
}, nodePin(this.deps.ownerNodeId)), ATTACH_RPC_TIMEOUT_MS, "getStreamWithCodec"));
|
|
15379
15214
|
const streamAcquiredMs = this.now();
|
|
15380
|
-
const rtspUrl = withRecordingIntent(source.url);
|
|
15215
|
+
const rtspUrl = withRecordingIntent(source.url, RECORDING_PRE_ROLL_REQUEST_MS);
|
|
15381
15216
|
this.attachSeq++;
|
|
15382
15217
|
const consumerTag = `recorder/${deviceId}/${profile}/${this.attachSeq}`;
|
|
15383
15218
|
if (rtspUrl === source.url) this.deps.logger.info("recorder: dial carries no recording intent — no pre-roll", {
|