@camera.ui/browser 0.1.4 → 0.1.6
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 +64 -10
- package/package.json +9 -9
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,9 @@ 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 serverClockOffset = 0;
|
|
676
680
|
function notify(cameraId) {
|
|
677
681
|
subscribers.get(cameraId)?.forEach((cb) => cb());
|
|
678
682
|
}
|
|
@@ -719,6 +723,25 @@ function isOutdated(settings, fetchedAt) {
|
|
|
719
723
|
function deferRevoke(url) {
|
|
720
724
|
setTimeout(() => URL.revokeObjectURL(url), REVOKE_DELAY_MS);
|
|
721
725
|
}
|
|
726
|
+
function startSnapshotRefresh(cameraId) {
|
|
727
|
+
clearTimeout(refreshLingers.get(cameraId));
|
|
728
|
+
refreshLingers.delete(cameraId);
|
|
729
|
+
refreshingCameras.add(cameraId);
|
|
730
|
+
}
|
|
731
|
+
function endSnapshotRefresh(cameraId) {
|
|
732
|
+
clearTimeout(refreshLingers.get(cameraId));
|
|
733
|
+
const linger = setTimeout(() => {
|
|
734
|
+
refreshLingers.delete(cameraId);
|
|
735
|
+
refreshingCameras.delete(cameraId);
|
|
736
|
+
}, REFRESH_LINGER_MS);
|
|
737
|
+
refreshLingers.set(cameraId, linger);
|
|
738
|
+
}
|
|
739
|
+
function rememberServerClock(browserTime, serverTime) {
|
|
740
|
+
serverClockOffset = browserTime - serverTime;
|
|
741
|
+
}
|
|
742
|
+
function toBrowserTime(serverTime) {
|
|
743
|
+
return serverTime === void 0 ? void 0 : serverTime + serverClockOffset;
|
|
744
|
+
}
|
|
722
745
|
function isOlderThanHeld(cameraId, takenAt, stamp) {
|
|
723
746
|
const held = stampCache.get(cameraId);
|
|
724
747
|
if (stamp && held) {
|
|
@@ -739,6 +762,8 @@ function setSnapshot(cameraId, data, fetchedAt, takenAt, stamp) {
|
|
|
739
762
|
urlCache.delete(cameraId);
|
|
740
763
|
}
|
|
741
764
|
snapshotCache.set(cameraId, data);
|
|
765
|
+
startSnapshotRefresh(cameraId);
|
|
766
|
+
endSnapshotRefresh(cameraId);
|
|
742
767
|
}
|
|
743
768
|
if (fetchedAt !== void 0) timestampCache.set(cameraId, fetchedAt);
|
|
744
769
|
notify(cameraId);
|
|
@@ -769,17 +794,21 @@ function getSnapshotUrl(cameraId) {
|
|
|
769
794
|
}
|
|
770
795
|
function clearSnapshotCache() {
|
|
771
796
|
for (const url of urlCache.values()) URL.revokeObjectURL(url);
|
|
797
|
+
for (const linger of refreshLingers.values()) clearTimeout(linger);
|
|
772
798
|
urlCache.clear();
|
|
773
799
|
snapshotCache.clear();
|
|
774
800
|
timestampCache.clear();
|
|
775
801
|
takenAtCache.clear();
|
|
776
802
|
stampCache.clear();
|
|
803
|
+
refreshLingers.clear();
|
|
804
|
+
refreshingCameras.clear();
|
|
805
|
+
serverClockOffset = 0;
|
|
777
806
|
}
|
|
778
807
|
function useSnapshot(cameraIdOrName) {
|
|
779
808
|
const cameraUi = useCameraUi();
|
|
780
809
|
const { isConnected } = cameraUi;
|
|
781
810
|
const deviceManager = useDeviceManager();
|
|
782
|
-
const { onTabVisible } = useTabVisibility();
|
|
811
|
+
const { isVisible, onTabVisible } = useTabVisibility();
|
|
783
812
|
const snapshot = shallowRef();
|
|
784
813
|
const _isLoading = ref(false);
|
|
785
814
|
const initialLoadDone = ref(false);
|
|
@@ -799,6 +828,7 @@ function useSnapshot(cameraIdOrName) {
|
|
|
799
828
|
if (!subscribers.has(cameraId)) subscribers.set(cameraId, /* @__PURE__ */ new Set());
|
|
800
829
|
const updateFn = () => {
|
|
801
830
|
snapshot.value = snapshotCache.get(cameraId);
|
|
831
|
+
armViewRefresh(cameraId);
|
|
802
832
|
};
|
|
803
833
|
subscribers.get(cameraId).add(updateFn);
|
|
804
834
|
return () => {
|
|
@@ -809,6 +839,7 @@ function useSnapshot(cameraIdOrName) {
|
|
|
809
839
|
let heldDeviceId;
|
|
810
840
|
let heldDevice;
|
|
811
841
|
let pendingHoldId;
|
|
842
|
+
let viewRefreshTimer;
|
|
812
843
|
const holdRetry = createRetryTimer();
|
|
813
844
|
let disposed = false;
|
|
814
845
|
function currentId() {
|
|
@@ -829,6 +860,7 @@ function useSnapshot(cameraIdOrName) {
|
|
|
829
860
|
heldDeviceId = id;
|
|
830
861
|
heldDevice = device;
|
|
831
862
|
cancelHoldRetry();
|
|
863
|
+
armViewRefresh(id);
|
|
832
864
|
} catch {
|
|
833
865
|
if (currentId() === id) scheduleHoldRetry(id);
|
|
834
866
|
} finally {
|
|
@@ -849,12 +881,32 @@ function useSnapshot(cameraIdOrName) {
|
|
|
849
881
|
holdRetry.reset();
|
|
850
882
|
}
|
|
851
883
|
function releaseHeldDevice() {
|
|
884
|
+
clearTimeout(viewRefreshTimer);
|
|
852
885
|
if (heldDeviceId) {
|
|
853
886
|
if (heldDevice) releaseCameraDevice(heldDevice);
|
|
854
887
|
heldDeviceId = void 0;
|
|
855
888
|
heldDevice = void 0;
|
|
856
889
|
}
|
|
857
890
|
}
|
|
891
|
+
function refreshIfOutdated(id) {
|
|
892
|
+
if (!isConnected.value || !heldDevice || heldDeviceId !== id) return;
|
|
893
|
+
if (!isOutdated(heldDevice.camera.value.snapshotSettings, timestampCache.get(id))) return;
|
|
894
|
+
fetchSnapshotShared(deviceManager, id).catch(() => void 0);
|
|
895
|
+
}
|
|
896
|
+
function armViewRefresh(id) {
|
|
897
|
+
clearTimeout(viewRefreshTimer);
|
|
898
|
+
const settings = heldDevice?.camera.value.snapshotSettings;
|
|
899
|
+
if (heldDeviceId !== id || settings?.mode !== "onView") return;
|
|
900
|
+
const maxAgeMs = settings.maxAge * 1e3;
|
|
901
|
+
const leftMs = (timestampCache.get(id) ?? 0) + maxAgeMs - Date.now();
|
|
902
|
+
viewRefreshTimer = setTimeout(() => {
|
|
903
|
+
refreshOnView(id);
|
|
904
|
+
}, leftMs > 0 ? leftMs : maxAgeMs);
|
|
905
|
+
}
|
|
906
|
+
async function refreshOnView(id) {
|
|
907
|
+
if (isVisible.value && isConnected.value) await fetchSnapshotShared(deviceManager, id).catch(() => void 0);
|
|
908
|
+
armViewRefresh(id);
|
|
909
|
+
}
|
|
858
910
|
function showCached(id) {
|
|
859
911
|
const cached = snapshotCache.get(id);
|
|
860
912
|
if (!cached) return false;
|
|
@@ -864,7 +916,10 @@ function useSnapshot(cameraIdOrName) {
|
|
|
864
916
|
}
|
|
865
917
|
async function loadSnapshot(id, skipCache = false) {
|
|
866
918
|
if (!isConnected.value || !id) return;
|
|
867
|
-
if (!skipCache && showCached(id))
|
|
919
|
+
if (!skipCache && showCached(id)) {
|
|
920
|
+
refreshIfOutdated(id);
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
868
923
|
_isLoading.value = true;
|
|
869
924
|
try {
|
|
870
925
|
const result = await fetchSnapshotShared(deviceManager, id);
|
|
@@ -915,12 +970,7 @@ function useSnapshot(cameraIdOrName) {
|
|
|
915
970
|
snapshot.value = void 0;
|
|
916
971
|
}
|
|
917
972
|
}, { 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
|
-
});
|
|
973
|
+
onTabVisible(() => refreshIfOutdated(currentId()));
|
|
924
974
|
tryOnScopeDispose(() => {
|
|
925
975
|
disposed = true;
|
|
926
976
|
cancelHoldRetry();
|
|
@@ -932,6 +982,7 @@ function useSnapshot(cameraIdOrName) {
|
|
|
932
982
|
snapshotSrc,
|
|
933
983
|
snapshotTimestamp,
|
|
934
984
|
isLoading: computed(() => _isLoading.value || !initialLoadDone.value),
|
|
985
|
+
isRefreshing: computed(() => refreshingCameras.has(currentId())),
|
|
935
986
|
refresh
|
|
936
987
|
};
|
|
937
988
|
}
|
|
@@ -999,6 +1050,7 @@ async function createReactiveCameraDevice(rpcOrContext, initialCamera) {
|
|
|
999
1050
|
const hasPtz = computed(() => existingSensorTypes.value.has(SensorType.PTZ));
|
|
1000
1051
|
async function fetchSnapshot(sourceId, forceNew) {
|
|
1001
1052
|
snapshotLoading.value = true;
|
|
1053
|
+
startSnapshotRefresh(initialCamera._id);
|
|
1002
1054
|
const requestedAt = Date.now();
|
|
1003
1055
|
try {
|
|
1004
1056
|
const result = await requestSnapshot(sourceId, forceNew);
|
|
@@ -1008,12 +1060,14 @@ async function createReactiveCameraDevice(rpcOrContext, initialCamera) {
|
|
|
1008
1060
|
sourceId,
|
|
1009
1061
|
fetchedAt: result.stamp
|
|
1010
1062
|
};
|
|
1063
|
+
if (stamp && result.fetchedAt !== void 0) rememberServerClock(result.fetchedAt, stamp.fetchedAt);
|
|
1011
1064
|
setSnapshot(initialCamera._id, result.data, result.fetchedAt, result.fetchedAt ?? requestedAt, stamp);
|
|
1012
1065
|
return result.data;
|
|
1013
1066
|
}
|
|
1014
1067
|
return;
|
|
1015
1068
|
} finally {
|
|
1016
1069
|
snapshotLoading.value = false;
|
|
1070
|
+
endSnapshotRefresh(initialCamera._id);
|
|
1017
1071
|
}
|
|
1018
1072
|
}
|
|
1019
1073
|
async function requestSnapshot(sourceId, forceNew) {
|
|
@@ -1139,7 +1193,7 @@ async function createReactiveCameraDevice(rpcOrContext, initialCamera) {
|
|
|
1139
1193
|
break;
|
|
1140
1194
|
case "snapshot:updated": {
|
|
1141
1195
|
const { sourceId, snapshot: data, fetchedAt } = event.data;
|
|
1142
|
-
setSnapshot(initialCamera._id, data, fetchedAt, void 0, sourceId && fetchedAt !== void 0 ? {
|
|
1196
|
+
setSnapshot(initialCamera._id, data, toBrowserTime(fetchedAt), void 0, sourceId && fetchedAt !== void 0 ? {
|
|
1143
1197
|
sourceId,
|
|
1144
1198
|
fetchedAt
|
|
1145
1199
|
} : void 0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camera.ui/browser",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "camera.ui browser client",
|
|
5
5
|
"author": "seydx (https://github.com/cameraui/clients)",
|
|
6
6
|
"type": "module",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"prepublishOnly": "npm i --package-lock-only && npm run lint && npm run format && npm run build",
|
|
27
27
|
"test": "vitest run --passWithNoTests",
|
|
28
28
|
"test:watch": "vitest",
|
|
29
|
-
"update": "updates -M npm,pypi,go,cargo,docker
|
|
29
|
+
"update": "updates -M npm,pypi,go,cargo,docker --update ./"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"@camera.ui/logger": ">=0.0.3",
|
|
@@ -39,29 +39,29 @@
|
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@camera.ui/logger": "file:../../packages/logger",
|
|
41
41
|
"@camera.ui/rpc": "^1.1.1",
|
|
42
|
-
"@camera.ui/sdk": "~1.4.
|
|
42
|
+
"@camera.ui/sdk": "~1.4.5",
|
|
43
43
|
"@camera.ui/transport": "file:../../packages/transport",
|
|
44
44
|
"@eneris/push-receiver": "^4.4.0",
|
|
45
|
-
"@microsoft/api-extractor": "^7.
|
|
45
|
+
"@microsoft/api-extractor": "^7.60.1",
|
|
46
46
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
47
47
|
"@types/webrtc": "^0.0.47",
|
|
48
|
-
"@typescript-eslint/parser": "^8.72.
|
|
48
|
+
"@typescript-eslint/parser": "^8.72.1",
|
|
49
49
|
"@vitejs/plugin-vue": "^6.0.10",
|
|
50
50
|
"@vue/eslint-config-prettier": "^10.2.0",
|
|
51
51
|
"@vue/eslint-config-typescript": "^14.9.0",
|
|
52
|
-
"@vue/language-core": "^3.3.
|
|
52
|
+
"@vue/language-core": "^3.3.14",
|
|
53
53
|
"@vue/tsconfig": "^0.9.1",
|
|
54
54
|
"@vueuse/core": "^15.0.0",
|
|
55
55
|
"@webgpu/types": "^0.1.74",
|
|
56
56
|
"eslint": "9.39.2",
|
|
57
|
-
"eslint-plugin-vue": "^10.
|
|
57
|
+
"eslint-plugin-vue": "^10.12.0",
|
|
58
58
|
"globals": "^17.13.0",
|
|
59
59
|
"prettier": "^3.9.10",
|
|
60
60
|
"rimraf": "^6.1.3",
|
|
61
61
|
"typescript": "5.9.3",
|
|
62
|
-
"typescript-eslint": "^8.72.
|
|
62
|
+
"typescript-eslint": "^8.72.1",
|
|
63
63
|
"unplugin-dts": "^1.1.2",
|
|
64
|
-
"updates": "^
|
|
64
|
+
"updates": "^20.0.0",
|
|
65
65
|
"vite": "^8.3.5",
|
|
66
66
|
"vitest": "^5.0.5",
|
|
67
67
|
"vue": "^3.5.44",
|