@camera.ui/browser 0.1.5 → 0.1.7
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 +1 -0
- package/dist/index.js +85 -12
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1601,6 +1601,7 @@ export declare interface UseSnapshotReturn {
|
|
|
1601
1601
|
snapshotSrc: ComputedRef<string | undefined>;
|
|
1602
1602
|
snapshotTimestamp: ComputedRef<number | undefined>;
|
|
1603
1603
|
isLoading: ComputedRef<boolean>;
|
|
1604
|
+
isRefreshing: ComputedRef<boolean>;
|
|
1604
1605
|
refresh: () => Promise<void>;
|
|
1605
1606
|
}
|
|
1606
1607
|
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Logger } from "@camera.ui/logger";
|
|
2
|
-
import { computed, effectScope, inject, markRaw, onBeforeUnmount, readonly, ref, shallowReactive, shallowRef, toValue, watch } from "vue";
|
|
2
|
+
import { computed, effectScope, inject, markRaw, onBeforeUnmount, reactive, readonly, ref, shallowReactive, shallowRef, toValue, watch } from "vue";
|
|
3
3
|
import { tryOnScopeDispose, useTimeoutFn, whenever } from "@vueuse/core";
|
|
4
4
|
import { SensorType } from "@camera.ui/sdk";
|
|
5
5
|
import { isNoRespondersError } from "@camera.ui/rpc";
|
|
@@ -666,6 +666,7 @@ function useTabVisibility() {
|
|
|
666
666
|
//#region src/composables/useSnapshot.ts
|
|
667
667
|
var REVOKE_DELAY_MS = 5e3;
|
|
668
668
|
var SERVER_CLOCK_STEP_MS = 6e4;
|
|
669
|
+
var REFRESH_LINGER_MS = 500;
|
|
669
670
|
var snapshotCache = /* @__PURE__ */ new Map();
|
|
670
671
|
var timestampCache = /* @__PURE__ */ new Map();
|
|
671
672
|
var takenAtCache = /* @__PURE__ */ new Map();
|
|
@@ -673,6 +674,10 @@ var stampCache = /* @__PURE__ */ new Map();
|
|
|
673
674
|
var urlCache = /* @__PURE__ */ new Map();
|
|
674
675
|
var subscribers = /* @__PURE__ */ new Map();
|
|
675
676
|
var pendingLoads$2 = /* @__PURE__ */ new Map();
|
|
677
|
+
var refreshingCameras = reactive(/* @__PURE__ */ new Set());
|
|
678
|
+
var refreshLingers = /* @__PURE__ */ new Map();
|
|
679
|
+
var forcedRefreshes = /* @__PURE__ */ new Map();
|
|
680
|
+
var serverClockOffset = 0;
|
|
676
681
|
function notify(cameraId) {
|
|
677
682
|
subscribers.get(cameraId)?.forEach((cb) => cb());
|
|
678
683
|
}
|
|
@@ -687,7 +692,9 @@ function fetchSnapshotShared(deviceManager, id, forceFresh = false) {
|
|
|
687
692
|
} finally {
|
|
688
693
|
if (device) releaseCameraDevice(device);
|
|
689
694
|
}
|
|
690
|
-
})().finally(() =>
|
|
695
|
+
})().finally(() => {
|
|
696
|
+
if (pendingLoads$2.get(id) === promise) pendingLoads$2.delete(id);
|
|
697
|
+
});
|
|
691
698
|
pendingLoads$2.set(id, promise);
|
|
692
699
|
return promise;
|
|
693
700
|
}
|
|
@@ -696,6 +703,7 @@ function hookReconnectRefresh(cameraUi, deviceManager) {
|
|
|
696
703
|
if (hookedContexts.has(cameraUi)) return;
|
|
697
704
|
hookedContexts.add(cameraUi);
|
|
698
705
|
cameraUi.on("reconnected", () => {
|
|
706
|
+
pendingLoads$2.clear();
|
|
699
707
|
setTimeout(() => {
|
|
700
708
|
let delay = 0;
|
|
701
709
|
for (const id of subscribers.keys()) {
|
|
@@ -719,6 +727,32 @@ function isOutdated(settings, fetchedAt) {
|
|
|
719
727
|
function deferRevoke(url) {
|
|
720
728
|
setTimeout(() => URL.revokeObjectURL(url), REVOKE_DELAY_MS);
|
|
721
729
|
}
|
|
730
|
+
function startSnapshotRefresh(cameraId, forced = false) {
|
|
731
|
+
clearTimeout(refreshLingers.get(cameraId));
|
|
732
|
+
refreshLingers.delete(cameraId);
|
|
733
|
+
if (forced) forcedRefreshes.set(cameraId, (forcedRefreshes.get(cameraId) ?? 0) + 1);
|
|
734
|
+
refreshingCameras.add(cameraId);
|
|
735
|
+
}
|
|
736
|
+
function endSnapshotRefresh(cameraId, forced = false) {
|
|
737
|
+
const running = (forcedRefreshes.get(cameraId) ?? 0) - (forced ? 1 : 0);
|
|
738
|
+
if (running > 0) {
|
|
739
|
+
forcedRefreshes.set(cameraId, running);
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
forcedRefreshes.delete(cameraId);
|
|
743
|
+
clearTimeout(refreshLingers.get(cameraId));
|
|
744
|
+
const linger = setTimeout(() => {
|
|
745
|
+
refreshLingers.delete(cameraId);
|
|
746
|
+
refreshingCameras.delete(cameraId);
|
|
747
|
+
}, REFRESH_LINGER_MS);
|
|
748
|
+
refreshLingers.set(cameraId, linger);
|
|
749
|
+
}
|
|
750
|
+
function rememberServerClock(browserTime, serverTime) {
|
|
751
|
+
serverClockOffset = browserTime - serverTime;
|
|
752
|
+
}
|
|
753
|
+
function toBrowserTime(serverTime) {
|
|
754
|
+
return serverTime === void 0 ? void 0 : serverTime + serverClockOffset;
|
|
755
|
+
}
|
|
722
756
|
function isOlderThanHeld(cameraId, takenAt, stamp) {
|
|
723
757
|
const held = stampCache.get(cameraId);
|
|
724
758
|
if (stamp && held) {
|
|
@@ -739,6 +773,8 @@ function setSnapshot(cameraId, data, fetchedAt, takenAt, stamp) {
|
|
|
739
773
|
urlCache.delete(cameraId);
|
|
740
774
|
}
|
|
741
775
|
snapshotCache.set(cameraId, data);
|
|
776
|
+
startSnapshotRefresh(cameraId);
|
|
777
|
+
endSnapshotRefresh(cameraId);
|
|
742
778
|
}
|
|
743
779
|
if (fetchedAt !== void 0) timestampCache.set(cameraId, fetchedAt);
|
|
744
780
|
notify(cameraId);
|
|
@@ -769,17 +805,22 @@ function getSnapshotUrl(cameraId) {
|
|
|
769
805
|
}
|
|
770
806
|
function clearSnapshotCache() {
|
|
771
807
|
for (const url of urlCache.values()) URL.revokeObjectURL(url);
|
|
808
|
+
for (const linger of refreshLingers.values()) clearTimeout(linger);
|
|
772
809
|
urlCache.clear();
|
|
773
810
|
snapshotCache.clear();
|
|
774
811
|
timestampCache.clear();
|
|
775
812
|
takenAtCache.clear();
|
|
776
813
|
stampCache.clear();
|
|
814
|
+
refreshLingers.clear();
|
|
815
|
+
forcedRefreshes.clear();
|
|
816
|
+
refreshingCameras.clear();
|
|
817
|
+
serverClockOffset = 0;
|
|
777
818
|
}
|
|
778
819
|
function useSnapshot(cameraIdOrName) {
|
|
779
820
|
const cameraUi = useCameraUi();
|
|
780
821
|
const { isConnected } = cameraUi;
|
|
781
822
|
const deviceManager = useDeviceManager();
|
|
782
|
-
const { onTabVisible } = useTabVisibility();
|
|
823
|
+
const { isVisible, onTabVisible } = useTabVisibility();
|
|
783
824
|
const snapshot = shallowRef();
|
|
784
825
|
const _isLoading = ref(false);
|
|
785
826
|
const initialLoadDone = ref(false);
|
|
@@ -799,6 +840,7 @@ function useSnapshot(cameraIdOrName) {
|
|
|
799
840
|
if (!subscribers.has(cameraId)) subscribers.set(cameraId, /* @__PURE__ */ new Set());
|
|
800
841
|
const updateFn = () => {
|
|
801
842
|
snapshot.value = snapshotCache.get(cameraId);
|
|
843
|
+
armViewRefresh(cameraId);
|
|
802
844
|
};
|
|
803
845
|
subscribers.get(cameraId).add(updateFn);
|
|
804
846
|
return () => {
|
|
@@ -809,6 +851,7 @@ function useSnapshot(cameraIdOrName) {
|
|
|
809
851
|
let heldDeviceId;
|
|
810
852
|
let heldDevice;
|
|
811
853
|
let pendingHoldId;
|
|
854
|
+
let viewRefreshTimer;
|
|
812
855
|
const holdRetry = createRetryTimer();
|
|
813
856
|
let disposed = false;
|
|
814
857
|
function currentId() {
|
|
@@ -829,6 +872,7 @@ function useSnapshot(cameraIdOrName) {
|
|
|
829
872
|
heldDeviceId = id;
|
|
830
873
|
heldDevice = device;
|
|
831
874
|
cancelHoldRetry();
|
|
875
|
+
armViewRefresh(id);
|
|
832
876
|
} catch {
|
|
833
877
|
if (currentId() === id) scheduleHoldRetry(id);
|
|
834
878
|
} finally {
|
|
@@ -849,12 +893,32 @@ function useSnapshot(cameraIdOrName) {
|
|
|
849
893
|
holdRetry.reset();
|
|
850
894
|
}
|
|
851
895
|
function releaseHeldDevice() {
|
|
896
|
+
clearTimeout(viewRefreshTimer);
|
|
852
897
|
if (heldDeviceId) {
|
|
853
898
|
if (heldDevice) releaseCameraDevice(heldDevice);
|
|
854
899
|
heldDeviceId = void 0;
|
|
855
900
|
heldDevice = void 0;
|
|
856
901
|
}
|
|
857
902
|
}
|
|
903
|
+
function refreshIfOutdated(id) {
|
|
904
|
+
if (!isConnected.value || !heldDevice || heldDeviceId !== id) return;
|
|
905
|
+
if (!isOutdated(heldDevice.camera.value.snapshotSettings, timestampCache.get(id))) return;
|
|
906
|
+
fetchSnapshotShared(deviceManager, id).catch(() => void 0);
|
|
907
|
+
}
|
|
908
|
+
function armViewRefresh(id) {
|
|
909
|
+
clearTimeout(viewRefreshTimer);
|
|
910
|
+
const settings = heldDevice?.camera.value.snapshotSettings;
|
|
911
|
+
if (heldDeviceId !== id || settings?.mode !== "onView") return;
|
|
912
|
+
const maxAgeMs = settings.maxAge * 1e3;
|
|
913
|
+
const leftMs = (timestampCache.get(id) ?? 0) + maxAgeMs - Date.now();
|
|
914
|
+
viewRefreshTimer = setTimeout(() => {
|
|
915
|
+
refreshOnView(id);
|
|
916
|
+
}, leftMs > 0 ? leftMs : maxAgeMs);
|
|
917
|
+
}
|
|
918
|
+
async function refreshOnView(id) {
|
|
919
|
+
if (isVisible.value && isConnected.value) await fetchSnapshotShared(deviceManager, id).catch(() => void 0);
|
|
920
|
+
armViewRefresh(id);
|
|
921
|
+
}
|
|
858
922
|
function showCached(id) {
|
|
859
923
|
const cached = snapshotCache.get(id);
|
|
860
924
|
if (!cached) return false;
|
|
@@ -864,7 +928,10 @@ function useSnapshot(cameraIdOrName) {
|
|
|
864
928
|
}
|
|
865
929
|
async function loadSnapshot(id, skipCache = false) {
|
|
866
930
|
if (!isConnected.value || !id) return;
|
|
867
|
-
if (!skipCache && showCached(id))
|
|
931
|
+
if (!skipCache && showCached(id)) {
|
|
932
|
+
refreshIfOutdated(id);
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
868
935
|
_isLoading.value = true;
|
|
869
936
|
try {
|
|
870
937
|
const result = await fetchSnapshotShared(deviceManager, id);
|
|
@@ -915,12 +982,7 @@ function useSnapshot(cameraIdOrName) {
|
|
|
915
982
|
snapshot.value = void 0;
|
|
916
983
|
}
|
|
917
984
|
}, { immediate: true });
|
|
918
|
-
onTabVisible(() =>
|
|
919
|
-
const id = currentId();
|
|
920
|
-
if (!isConnected.value || !heldDevice || heldDeviceId !== id) return;
|
|
921
|
-
if (!isOutdated(heldDevice.camera.value.snapshotSettings, timestampCache.get(id))) return;
|
|
922
|
-
fetchSnapshotShared(deviceManager, id).catch(() => void 0);
|
|
923
|
-
});
|
|
985
|
+
onTabVisible(() => refreshIfOutdated(currentId()));
|
|
924
986
|
tryOnScopeDispose(() => {
|
|
925
987
|
disposed = true;
|
|
926
988
|
cancelHoldRetry();
|
|
@@ -932,14 +994,22 @@ function useSnapshot(cameraIdOrName) {
|
|
|
932
994
|
snapshotSrc,
|
|
933
995
|
snapshotTimestamp,
|
|
934
996
|
isLoading: computed(() => _isLoading.value || !initialLoadDone.value),
|
|
997
|
+
isRefreshing: computed(() => refreshingCameras.has(currentId())),
|
|
935
998
|
refresh
|
|
936
999
|
};
|
|
937
1000
|
}
|
|
938
1001
|
//#endregion
|
|
939
1002
|
//#region src/composables/useCamera.ts
|
|
940
1003
|
var NOT_MODIFIED = "not-modified";
|
|
1004
|
+
var CLOCK_SAMPLE_MAX_MS = 5e3;
|
|
941
1005
|
var snapshotWithMetaSupport = /* @__PURE__ */ new WeakMap();
|
|
942
1006
|
var sensorEventFanouts = /* @__PURE__ */ new WeakMap();
|
|
1007
|
+
function replyTime(result, requestedAt) {
|
|
1008
|
+
if (result.stamp === void 0 || result.fetchedAt === void 0) return result.fetchedAt;
|
|
1009
|
+
if (Date.now() - requestedAt > CLOCK_SAMPLE_MAX_MS) return toBrowserTime(result.stamp);
|
|
1010
|
+
rememberServerClock(result.fetchedAt, result.stamp);
|
|
1011
|
+
return result.fetchedAt;
|
|
1012
|
+
}
|
|
943
1013
|
async function createReactiveCameraDevice(rpcOrContext, initialCamera) {
|
|
944
1014
|
const cameraNamespaces = NamespaceManager.cameraNamespaces(initialCamera._id);
|
|
945
1015
|
const ctx = "rpc" in rpcOrContext && "value" in rpcOrContext.rpc ? rpcOrContext : void 0;
|
|
@@ -999,6 +1069,7 @@ async function createReactiveCameraDevice(rpcOrContext, initialCamera) {
|
|
|
999
1069
|
const hasPtz = computed(() => existingSensorTypes.value.has(SensorType.PTZ));
|
|
1000
1070
|
async function fetchSnapshot(sourceId, forceNew) {
|
|
1001
1071
|
snapshotLoading.value = true;
|
|
1072
|
+
startSnapshotRefresh(initialCamera._id, forceNew);
|
|
1002
1073
|
const requestedAt = Date.now();
|
|
1003
1074
|
try {
|
|
1004
1075
|
const result = await requestSnapshot(sourceId, forceNew);
|
|
@@ -1008,12 +1079,14 @@ async function createReactiveCameraDevice(rpcOrContext, initialCamera) {
|
|
|
1008
1079
|
sourceId,
|
|
1009
1080
|
fetchedAt: result.stamp
|
|
1010
1081
|
};
|
|
1011
|
-
|
|
1082
|
+
const fetchedAt = replyTime(result, requestedAt);
|
|
1083
|
+
setSnapshot(initialCamera._id, result.data, fetchedAt, fetchedAt ?? requestedAt, stamp);
|
|
1012
1084
|
return result.data;
|
|
1013
1085
|
}
|
|
1014
1086
|
return;
|
|
1015
1087
|
} finally {
|
|
1016
1088
|
snapshotLoading.value = false;
|
|
1089
|
+
endSnapshotRefresh(initialCamera._id, forceNew);
|
|
1017
1090
|
}
|
|
1018
1091
|
}
|
|
1019
1092
|
async function requestSnapshot(sourceId, forceNew) {
|
|
@@ -1139,7 +1212,7 @@ async function createReactiveCameraDevice(rpcOrContext, initialCamera) {
|
|
|
1139
1212
|
break;
|
|
1140
1213
|
case "snapshot:updated": {
|
|
1141
1214
|
const { sourceId, snapshot: data, fetchedAt } = event.data;
|
|
1142
|
-
setSnapshot(initialCamera._id, data, fetchedAt, void 0, sourceId && fetchedAt !== void 0 ? {
|
|
1215
|
+
setSnapshot(initialCamera._id, data, toBrowserTime(fetchedAt), void 0, sourceId && fetchedAt !== void 0 ? {
|
|
1143
1216
|
sourceId,
|
|
1144
1217
|
fetchedAt
|
|
1145
1218
|
} : void 0);
|