@camera.ui/browser 0.0.132 → 0.0.134

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/index.d.ts CHANGED
@@ -143,6 +143,7 @@ export declare interface CameraDeviceInterface extends CameraDeviceImplementatio
143
143
  refreshStates(): Promise<RefreshedStates>;
144
144
  connect(): Promise<void>;
145
145
  disconnect(): Promise<void>;
146
+ snapshotWithMeta(sourceId: string, forceNew?: boolean): Promise<SnapshotWithMeta | undefined>;
146
147
  probeStream(sourceId: string, probeConfig?: ProbeConfig, refresh?: boolean): Promise<ProbeStream | undefined>;
147
148
  getStreamStatus(sourceId: string): Promise<string>;
148
149
  registerSensor(sensor: SensorJSON, pluginId: string): Promise<boolean>;
@@ -971,6 +972,11 @@ declare interface SensorRemovedEvent {
971
972
 
972
973
  /* Excluded from this release type: SmokeSensorProperties */
973
974
 
975
+ declare interface SnapshotWithMeta {
976
+ data: ArrayBuffer;
977
+ ageMs: number;
978
+ }
979
+
974
980
  export declare interface StorageRPC {
975
981
  getValue<T = string>(key: string, defaultValue?: T): Promise<T | undefined>;
976
982
  setValue<T = string>(key: string, newValue: T): Promise<void>;
@@ -984,6 +990,7 @@ export declare interface StorageRPC {
984
990
 
985
991
  export declare interface StoredSensorData {
986
992
  id: string;
993
+ stableId: string;
987
994
  type: SensorType;
988
995
  name: string;
989
996
  displayName: string;
@@ -1216,6 +1223,7 @@ export declare function useSnapshot(cameraIdOrName: MaybeRefOrGetter<string | DB
1216
1223
  export declare interface UseSnapshotReturn {
1217
1224
  snapshot: ShallowRef<ArrayBuffer | undefined>;
1218
1225
  snapshotSrc: ComputedRef<string | undefined>;
1226
+ snapshotTimestamp: ComputedRef<number | undefined>;
1219
1227
  isLoading: ComputedRef<boolean>;
1220
1228
  refresh: () => Promise<void>;
1221
1229
  }
package/dist/index.js CHANGED
@@ -411,6 +411,7 @@ function makeAbortError() {
411
411
  //#region src/composables/useSnapshot.ts
412
412
  var REVOKE_DELAY_MS = 5e3;
413
413
  var snapshotCache = /* @__PURE__ */ new Map();
414
+ var timestampCache = /* @__PURE__ */ new Map();
414
415
  var urlCache = /* @__PURE__ */ new Map();
415
416
  var subscribers = /* @__PURE__ */ new Map();
416
417
  function notify(cameraId) {
@@ -419,13 +420,14 @@ function notify(cameraId) {
419
420
  function deferRevoke(url) {
420
421
  setTimeout(() => URL.revokeObjectURL(url), REVOKE_DELAY_MS);
421
422
  }
422
- function setSnapshot(cameraId, data) {
423
+ function setSnapshot(cameraId, data, fetchedAt) {
423
424
  const oldUrl = urlCache.get(cameraId);
424
425
  if (oldUrl) {
425
426
  deferRevoke(oldUrl);
426
427
  urlCache.delete(cameraId);
427
428
  }
428
429
  snapshotCache.set(cameraId, data);
430
+ if (fetchedAt !== void 0) timestampCache.set(cameraId, fetchedAt);
429
431
  notify(cameraId);
430
432
  }
431
433
  function getSnapshot(cameraId) {
@@ -452,6 +454,7 @@ function clearSnapshotCache() {
452
454
  for (const url of urlCache.values()) URL.revokeObjectURL(url);
453
455
  urlCache.clear();
454
456
  snapshotCache.clear();
457
+ timestampCache.clear();
455
458
  }
456
459
  function useSnapshot(cameraIdOrName) {
457
460
  const { isConnected } = useCameraUi();
@@ -464,6 +467,12 @@ function useSnapshot(cameraIdOrName) {
464
467
  const idOrName = toValue(cameraIdOrName);
465
468
  return getSnapshotUrl(typeof idOrName === "string" ? idOrName : idOrName._id);
466
469
  });
470
+ const snapshotTimestamp = computed(() => {
471
+ if (!snapshot.value) return void 0;
472
+ const idOrName = toValue(cameraIdOrName);
473
+ const id = typeof idOrName === "string" ? idOrName : idOrName._id;
474
+ return timestampCache.get(id);
475
+ });
467
476
  function subscribe(cameraId) {
468
477
  if (!subscribers.has(cameraId)) subscribers.set(cameraId, /* @__PURE__ */ new Set());
469
478
  const updateFn = () => {
@@ -475,6 +484,30 @@ function useSnapshot(cameraIdOrName) {
475
484
  };
476
485
  }
477
486
  let unsubscribe;
487
+ let heldDeviceId;
488
+ let pendingHoldId;
489
+ async function holdDevice(id) {
490
+ if (heldDeviceId === id || pendingHoldId === id) return;
491
+ pendingHoldId = id;
492
+ try {
493
+ if (!await acquireCameraDevice(deviceManager, id)) return;
494
+ const idOrName = toValue(cameraIdOrName);
495
+ if ((typeof idOrName === "string" ? idOrName : idOrName._id) !== id || heldDeviceId === id) {
496
+ releaseCameraDevice(id);
497
+ return;
498
+ }
499
+ releaseHeldDevice();
500
+ heldDeviceId = id;
501
+ } catch {} finally {
502
+ pendingHoldId = void 0;
503
+ }
504
+ }
505
+ function releaseHeldDevice() {
506
+ if (heldDeviceId) {
507
+ releaseCameraDevice(heldDeviceId);
508
+ heldDeviceId = void 0;
509
+ }
510
+ }
478
511
  async function loadSnapshot(id) {
479
512
  if (!isConnected.value || !id) return;
480
513
  const cached = snapshotCache.get(id);
@@ -539,18 +572,25 @@ function useSnapshot(cameraIdOrName) {
539
572
  const id = typeof cameraIdName === "string" ? cameraIdName : cameraIdName._id;
540
573
  if (connected && id && !disabled) {
541
574
  unsubscribe = subscribe(id);
575
+ await holdDevice(id);
542
576
  await loadSnapshot(id);
543
577
  } else if (id) {
578
+ releaseHeldDevice();
544
579
  snapshot.value = snapshotCache.get(id);
545
580
  initialLoadDone.value = true;
546
- } else snapshot.value = void 0;
581
+ } else {
582
+ releaseHeldDevice();
583
+ snapshot.value = void 0;
584
+ }
547
585
  }, { immediate: true });
548
586
  tryOnScopeDispose(() => {
549
587
  unsubscribe?.();
588
+ releaseHeldDevice();
550
589
  });
551
590
  return {
552
591
  snapshot,
553
592
  snapshotSrc,
593
+ snapshotTimestamp,
554
594
  isLoading: computed(() => _isLoading.value || !initialLoadDone.value),
555
595
  refresh
556
596
  };
@@ -608,10 +648,19 @@ async function createReactiveCameraDevice(rpcOrContext, initialCamera) {
608
648
  async function fetchSnapshot(sourceId, forceNew) {
609
649
  snapshotLoading.value = true;
610
650
  try {
611
- const result = await rpcCall(rpcRef, (client) => client.createProxy(cameraNamespaces.cameraControllerRpc).snapshot(sourceId, forceNew));
612
- if (result && result.byteLength > 0) {
613
- setSnapshot(initialCamera._id, result);
614
- return result;
651
+ try {
652
+ const result = await rpcCall(rpcRef, (client) => client.createProxy(cameraNamespaces.cameraControllerRpc).snapshotWithMeta(sourceId, forceNew));
653
+ if (result && result.data.byteLength > 0) {
654
+ setSnapshot(initialCamera._id, result.data, Date.now() - result.ageMs);
655
+ return result.data;
656
+ }
657
+ return;
658
+ } catch {
659
+ const result = await rpcCall(rpcRef, (client) => client.createProxy(cameraNamespaces.cameraControllerRpc).snapshot(sourceId, forceNew));
660
+ if (result && result.byteLength > 0) {
661
+ setSnapshot(initialCamera._id, result, forceNew ? Date.now() : void 0);
662
+ return result;
663
+ }
615
664
  }
616
665
  } finally {
617
666
  snapshotLoading.value = false;
@@ -645,7 +694,7 @@ async function createReactiveCameraDevice(rpcOrContext, initialCamera) {
645
694
  frameWorkerConnected.value = event.data;
646
695
  break;
647
696
  case "snapshot:updated":
648
- setSnapshot(initialCamera._id, event.data.snapshot);
697
+ setSnapshot(initialCamera._id, event.data.snapshot, Date.now());
649
698
  break;
650
699
  }
651
700
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camera.ui/browser",
3
- "version": "0.0.132",
3
+ "version": "0.0.134",
4
4
  "description": "camera.ui browser client",
5
5
  "author": "seydx (https://github.com/cameraui/clients)",
6
6
  "type": "module",
@@ -31,7 +31,7 @@
31
31
  "peerDependencies": {
32
32
  "@camera.ui/logger": ">=0.0.2",
33
33
  "@camera.ui/rpc": ">=1.0.9",
34
- "@camera.ui/sdk": ">=0.0.16",
34
+ "@camera.ui/sdk": ">=0.0.19",
35
35
  "@camera.ui/transport": ">=0.0.1",
36
36
  "@vueuse/core": ">=14.3.0",
37
37
  "vue": ">=3.5.39"
@@ -39,13 +39,13 @@
39
39
  "devDependencies": {
40
40
  "@camera.ui/logger": "file:../../packages/logger",
41
41
  "@camera.ui/rpc": "^1.0.9",
42
- "@camera.ui/sdk": "~0.0.16",
42
+ "@camera.ui/sdk": "~0.0.19",
43
43
  "@camera.ui/transport": "file:../../packages/transport",
44
44
  "@eneris/push-receiver": "^4.3.1",
45
45
  "@microsoft/api-extractor": "^7.58.9",
46
46
  "@stylistic/eslint-plugin": "^5.10.0",
47
47
  "@types/webrtc": "^0.0.47",
48
- "@typescript-eslint/parser": "^8.63.0",
48
+ "@typescript-eslint/parser": "^8.64.0",
49
49
  "@vitejs/plugin-vue": "^6.0.7",
50
50
  "@vue/eslint-config-prettier": "^10.2.0",
51
51
  "@vue/eslint-config-typescript": "^14.9.0",
@@ -59,7 +59,7 @@
59
59
  "prettier": "^3.9.5",
60
60
  "rimraf": "^6.1.3",
61
61
  "typescript": "5.9.3",
62
- "typescript-eslint": "^8.63.0",
62
+ "typescript-eslint": "^8.64.0",
63
63
  "unplugin-dts": "^1.0.3",
64
64
  "updates": "^17.18.2",
65
65
  "vite": "^8.1.4",