Compare commits

..

101 Commits

Author SHA1 Message Date
Koushik Dutta
59e1391fae fix restore on lxc 2024-01-30 10:03:31 -08:00
Koushik Dutta
e0a6e66e8a core: fix lag with terminal input 2024-01-29 22:17:08 -08:00
Koushik Dutta
fa7071b335 Merge branch 'main' of github.com:koush/scrypted 2024-01-29 21:02:05 -08:00
Koushik Dutta
c28e60d875 tensorflow-lite: switch default model to efficientdet_lite0_320_ptq 2024-01-29 21:02:00 -08:00
Koushik Dutta
62cbb88207 install: ensure local/docker services cant run concurrently 2024-01-29 18:21:14 -08:00
Brett Jia
4b6a858f2b ui: add client-side flow control (#1290) 2024-01-29 13:59:51 -08:00
Matthew Lieder
97a254b5d2 synology-ss: make login more resilient (#1289)
Fixes #1266
2024-01-28 15:27:02 -08:00
Koushik Dutta
0ada6286e7 openvino: update dep 2024-01-25 19:57:16 -08:00
Koushik Dutta
9f12e6dd6e ha: publish 2024-01-25 08:51:41 -08:00
Koushik Dutta
604798e845 docker: fixup /dev/dri enabling 2024-01-25 08:26:40 -08:00
Koushik Dutta
d912266de1 postrelease 2024-01-24 23:47:11 -08:00
Koushik Dutta
f5a32489d7 server: prevent windows from clobbering python path 2024-01-24 23:46:58 -08:00
Koushik Dutta
135ad8e3a8 rebroadcast: add id suffix to rtsp urls to determine ffmpeg usage 2024-01-24 13:52:35 -08:00
Koushik Dutta
3c4021c66b videoanalysis: disable filters for objects that are in detector provided zones 2024-01-23 21:42:36 -08:00
Koushik Dutta
669ab17772 docker: remove nvr storage config prompt 2024-01-23 15:47:48 -08:00
Koushik Dutta
1860d7d8ea Merge branch 'main' of github.com:koush/scrypted 2024-01-23 15:46:53 -08:00
Koushik Dutta
fa266e9dd1 docker: validate the storage directory 2024-01-23 20:10:02 +00:00
Koushik Dutta
4e2f3bf2c7 docker: finish drive setup script 2024-01-23 19:40:16 +00:00
Koushik Dutta
146e27fd13 install: initial pass at disk setup 2024-01-23 19:00:51 +00:00
Koushik Dutta
e4bb50375f postbeta 2024-01-22 20:15:16 -08:00
Koushik Dutta
9686315c02 postbeta 2024-01-22 19:57:24 -08:00
Koushik Dutta
520895f3aa postbeta 2024-01-22 19:50:20 -08:00
Koushik Dutta
ddffc49bcf postbeta 2024-01-22 19:22:20 -08:00
Koushik Dutta
a07f52445d postbeta 2024-01-22 19:13:41 -08:00
Koushik Dutta
5e7b203f11 postbeta 2024-01-22 19:01:13 -08:00
Koushik Dutta
d752298960 postbeta 2024-01-22 17:45:46 -08:00
Koushik Dutta
5253f29831 remove usage of NODE_* env variables which get sanitized by electron. 2024-01-22 17:45:26 -08:00
Koushik Dutta
58d674746d postrelease 2024-01-22 17:22:09 -08:00
Koushik Dutta
8a640758d1 server/cli: fix login issues 2024-01-22 09:11:02 -08:00
Koushik Dutta
9be913af26 sample-cameraprovider: update 2024-01-21 15:39:48 -08:00
Koushik Dutta
da17bee516 sdk: publish 2024-01-21 15:38:28 -08:00
Koushik Dutta
48d9790051 cli: fix https://github.com/koush/scrypted/issues/1277 2024-01-21 14:52:10 -08:00
Koushik Dutta
c43014348d sdk: prevent unnecessary JSON exceptions 2024-01-21 14:42:44 -08:00
Koushik Dutta
cec3a592ba client: add missing dependency 2024-01-21 14:42:24 -08:00
Koushik Dutta
c446ddcdf4 Merge branch 'main' of github.com:koush/scrypted 2024-01-21 12:45:24 -08:00
Koushik Dutta
72f79ea8ef core: fix certificate login error, fix backup/restore in ha 2024-01-21 12:45:19 -08:00
Brett Jia
41988699d0 server: expose backup as a service (#1275)
* server: expose backup as a service

* move restore into new backup service
2024-01-20 21:37:56 -08:00
Koushik Dutta
5151c520d4 Update config.yaml 2024-01-18 19:21:11 -08:00
Koushik Dutta
e1abe717fa postrelease 2024-01-18 13:30:02 -08:00
Koushik Dutta
c7a9ca06be server: use existing service control restart 2024-01-18 13:29:50 -08:00
Koushik Dutta
9827f15f5f server: pass through restart hook 2024-01-18 13:25:22 -08:00
Koushik Dutta
d245a722e2 postrelease 2024-01-18 13:20:25 -08:00
Koushik Dutta
c8e94c0386 Merge branch 'main' of github.com:koush/scrypted 2024-01-18 13:15:41 -08:00
Koushik Dutta
8c6e7b997a ui: implement backup/restore 2024-01-18 13:15:37 -08:00
Johannes Bosecker
9abc7ca139 amcrest: Implemented other intercom codec for Dahua doorbells (G.711A). (#1273) 2024-01-18 12:36:11 -08:00
Koushik Dutta
2a943eb5e0 postbeta 2024-01-18 09:33:13 -08:00
Koushik Dutta
a4fe78a48b ha: publish 2024-01-17 22:07:56 -08:00
Koushik Dutta
50ff0833c9 server: new node min verison 2024-01-17 10:26:30 -08:00
Koushik Dutta
c94085a6c7 zwave: smoke alarm support 2024-01-17 10:25:48 -08:00
Koushik Dutta
c477437456 server: add hook for restart 2024-01-14 15:47:25 -08:00
Koushik Dutta
0da96130fe Merge branch 'main' of github.com:koush/scrypted 2024-01-14 15:36:17 -08:00
Koushik Dutta
fdbf7ab60b server: implement backup/restore 2024-01-14 15:36:08 -08:00
Long Zheng
0cecfb86ff npm-install.sh install auth-fetch package (#1267) 2024-01-14 14:46:23 -08:00
Koushik Dutta
9a195c6207 homekit: fix prune crash 2024-01-14 14:25:30 -08:00
Koushik Dutta
47021a7743 server: reduce deps 2024-01-14 14:25:19 -08:00
Koushik Dutta
01400cf206 core: prep for server fakefs removal 2024-01-14 14:24:43 -08:00
Koushik Dutta
99da29a738 postrelease 2024-01-14 08:06:50 -08:00
Koushik Dutta
6378c5953a server: bump core 2024-01-14 08:06:42 -08:00
Koushik Dutta
846034d7c8 core: fix login 2024-01-14 08:06:25 -08:00
Koushik Dutta
ad47f14922 ha: publish 2024-01-13 22:45:05 -08:00
Koushik Dutta
0066379b1e postrelease 2024-01-13 22:18:49 -08:00
Koushik Dutta
54193251ab server: bump minimum core version 2024-01-13 22:17:40 -08:00
Koushik Dutta
cd5e169439 core: publish 2024-01-13 22:16:18 -08:00
Koushik Dutta
249a87bd4c cameras: fix validation skip, update auth lib 2024-01-13 19:29:35 -08:00
Koushik Dutta
803400c2d8 homekit: bump deps and publish 2024-01-13 18:34:04 -08:00
Koushik Dutta
0e700a53d0 snapshot: give periodic snapshots a moment to return 2024-01-13 17:05:26 -08:00
Koushik Dutta
72647b0099 Update Dockerfile.lite 2024-01-13 16:17:38 -08:00
Koushik Dutta
85e41180b2 Update docker-common.yml 2024-01-13 16:14:38 -08:00
Koushik Dutta
18bf012bb5 docker: udpate base 2024-01-13 16:12:43 -08:00
Koushik Dutta
3f2b8de169 Merge branch 'main' of github.com:koush/scrypted 2024-01-13 16:01:37 -08:00
Koushik Dutta
c568c9a37d shrink lite build further 2024-01-13 16:01:12 -08:00
Koushik Dutta
a831c48f5f server: fix stupid esm mime dependency 2024-01-12 18:22:41 -08:00
Koushik Dutta
f0357d45f2 server: fix stupid esm mime dependency 2024-01-12 18:14:31 -08:00
Koushik Dutta
479b3ce1f3 postrelease 2024-01-12 16:39:30 -08:00
Koushik Dutta
bd7c7de8a5 Update package.json 2024-01-12 16:37:55 -08:00
Koushik Dutta
a06e786d19 client: Fix cors credentials 2024-01-12 15:51:06 -08:00
Koushik Dutta
dff05e733e server: update lockfile 2024-01-12 15:39:29 -08:00
Koushik Dutta
76e34af149 postrelease 2024-01-12 15:13:29 -08:00
Koushik Dutta
4f2e9e88e1 Merge branch 'main' of github.com:koush/scrypted 2024-01-12 09:27:04 -08:00
Koushik Dutta
2761b0745a client: remove http-auth-utils dependency 2024-01-12 09:26:09 -08:00
Brett Jia
ea78b7f59e core: fix scheduler (#1259) 2024-01-12 09:10:31 -08:00
Koushik Dutta
b8d233f08d videoanalysis: cpu usage wip 2024-01-11 20:08:57 -08:00
Koushik Dutta
f4e93c82a2 onvif: publish with latest http-auth-utils 2024-01-11 20:08:39 -08:00
Koushik Dutta
c72c34f954 common: remove node-fetch 2024-01-11 20:06:51 -08:00
Koushik Dutta
baec4e71da remove axios-digest-auth 2024-01-11 19:31:18 -08:00
Koushik Dutta
245c6e3006 doorbird: remove axios digest auth 2024-01-11 19:29:15 -08:00
Koushik Dutta
566c18251c snapshot: add support for periodic snapshots via http 2024-01-11 13:15:18 -08:00
Koushik Dutta
f18e58a108 sdk/homekit/snapshot: simplify periodic snapshot requests. remove homekit debouncer. 2024-01-11 11:29:03 -08:00
Koushik Dutta
5c6ea09d3e videoanalysis: implement dynamic cpu performance profiling 2024-01-11 11:04:41 -08:00
Koushik Dutta
646a9f214a snapshot: Update url fetcher 2024-01-11 09:58:23 -08:00
Koushik Dutta
6506c4236f various: fixup http lib 2024-01-11 00:37:21 -08:00
Koushik Dutta
a3e27ce8f8 Merge branch 'main' of github.com:koush/scrypted 2024-01-11 00:35:26 -08:00
Koushik Dutta
a54d5a5f59 cli: remove axios 2024-01-11 00:35:21 -08:00
Koushik Dutta
7136759f8f Update install-scrypted-proxmox.sh 2024-01-11 00:32:48 -08:00
Koushik Dutta
049d9898b3 http/auth: simplify 2024-01-10 20:45:50 -08:00
Koushik Dutta
6d9e21b7b8 server: Fix import 2024-01-10 16:27:10 -08:00
Koushik Dutta
bfd1aef5d1 client: fixup 2024-01-10 16:23:51 -08:00
Koushik Dutta
5f02e6a272 various: more http refactoring 2024-01-10 16:08:38 -08:00
Koushik Dutta
56bbf00edc various: update http auth utils usage 2024-01-10 13:16:00 -08:00
Koushik Dutta
0c66456a87 sdk: limit chunks to 1. add support for multiple chunks. 2024-01-10 13:06:04 -08:00
Koushik Dutta
6b589b8d5a postrelease 2024-01-10 12:56:02 -08:00
131 changed files with 2850 additions and 1712 deletions

View File

@@ -15,7 +15,7 @@ jobs:
# "20"
]
BASE: ["jammy"]
FLAVOR: ["full", "lite", "thin"]
FLAVOR: ["full", "lite"]
steps:
- name: Check out the repo
uses: actions/checkout@v3

3
.gitmodules vendored
View File

@@ -7,9 +7,6 @@
[submodule "plugins/tensorflow/face-api.js"]
path = external/face-api.js
url = ../../koush/face-api.js
[submodule "external/axios-digest-auth"]
path = external/axios-digest-auth
url = ../../koush/axios-digest-auth
[submodule "external/scrypted-ffmpeg"]
path = external/scrypted-ffmpeg
url = ../../koush/scrypted-ffmpeg

166
common/package-lock.json generated
View File

@@ -11,12 +11,11 @@
"dependencies": {
"@scrypted/sdk": "file:../sdk",
"@scrypted/server": "file:../server",
"http-auth-utils": "^3.0.2",
"node-fetch-commonjs": "^3.1.1",
"http-auth-utils": "^5.0.1",
"typescript": "^5.3.3"
},
"devDependencies": {
"@types/node": "^20.10.8",
"@types/node": "^20.11.0",
"ts-node": "^10.9.2"
}
},
@@ -80,7 +79,7 @@
"dependencies": {
"@babel/preset-typescript": "^7.18.6",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"axios": "^1.6.5",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.15.9",
@@ -112,7 +111,7 @@
},
"../server": {
"name": "@scrypted/server",
"version": "0.81.0",
"version": "0.82.0",
"license": "ISC",
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.11",
@@ -242,9 +241,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "20.10.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.8.tgz",
"integrity": "sha512-f8nQs3cLxbAFc00vEU59yf9UyGUftkPaLGfvbVOIDdx2i1b8epBqj2aNGyP19fiyXWvlmZ7qC1XLjAzw/OKIeA==",
"version": "20.11.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.0.tgz",
"integrity": "sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
@@ -292,48 +291,15 @@
"node": ">=0.3.1"
}
},
"node_modules/fetch-blob": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.4.tgz",
"integrity": "sha512-Eq5Xv5+VlSrYWEqKrusxY1C3Hm/hjeAsCGVG3ft7pZahlUAChpGZT/Ms1WmSLnEAisEXszjzu/s+ce6HZB2VHA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"dependencies": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"dependencies": {
"fetch-blob": "^3.1.2"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/http-auth-utils": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/http-auth-utils/-/http-auth-utils-3.0.2.tgz",
"integrity": "sha512-cQ8957aiUX0lgV1620uIGKGJc0sEuD/QK4ueZ0hb60MGbO0f6ahcuIgPjamAD98D/AUGizKVm+dNvUVHs0f4Ow==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/http-auth-utils/-/http-auth-utils-5.0.1.tgz",
"integrity": "sha512-YPiLVYdwpBEWB85iWYg7V/ZW3mBfPLCTFQWEiPAA5CKXHJOAPbnJ0xDHLiE6KbPRvrYCwLuWqTG0fLfXVjMUcQ==",
"dependencies": {
"yerror": "^6.0.0"
"yerror": "^8.0.0"
},
"engines": {
"node": ">=12.19.0"
"node": ">=18.16.0"
}
},
"node_modules/make-error": {
@@ -342,40 +308,6 @@
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch-commonjs": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/node-fetch-commonjs/-/node-fetch-commonjs-3.1.1.tgz",
"integrity": "sha512-TgkdVJdiEaauzWwB9NoD4TvHZFtG6KKEffvotWf9WNIyoRZHsCFjGfb3bhkIXrMt3YFgFi8ZApbwWoe1h3XTpA==",
"dependencies": {
"formdata-polyfill": "^4.0.10",
"web-streams-polyfill": "^3.1.1"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/ts-node": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
@@ -443,20 +375,12 @@
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"dev": true
},
"node_modules/web-streams-polyfill": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz",
"integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==",
"engines": {
"node": ">= 8"
}
},
"node_modules/yerror": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/yerror/-/yerror-6.2.1.tgz",
"integrity": "sha512-WPZgybhCBzsMSSqGYBnj20NZo4FiFKQG0+i/21cYGVd4B7eYtvYDOpjk/0e8UM1eVHJ+4Ja6bZ7TAjHH6mk+ew==",
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/yerror/-/yerror-8.0.0.tgz",
"integrity": "sha512-FemWD5/UqNm8ffj8oZIbjWXIF2KE0mZssggYpdaQkWDDgXBQ/35PNIxEuz6/YLn9o0kOxDBNJe8x8k9ljD7k/g==",
"engines": {
"node": ">=12.19.0"
"node": ">=18.16.0"
}
},
"node_modules/yn": {
@@ -508,7 +432,7 @@
"@types/node": "^18.11.18",
"@types/stringify-object": "^4.0.0",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"axios": "^1.6.5",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.15.9",
@@ -603,9 +527,9 @@
"dev": true
},
"@types/node": {
"version": "20.10.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.8.tgz",
"integrity": "sha512-f8nQs3cLxbAFc00vEU59yf9UyGUftkPaLGfvbVOIDdx2i1b8epBqj2aNGyP19fiyXWvlmZ7qC1XLjAzw/OKIeA==",
"version": "20.11.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.0.tgz",
"integrity": "sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==",
"dev": true,
"requires": {
"undici-types": "~5.26.4"
@@ -641,29 +565,12 @@
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true
},
"fetch-blob": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.4.tgz",
"integrity": "sha512-Eq5Xv5+VlSrYWEqKrusxY1C3Hm/hjeAsCGVG3ft7pZahlUAChpGZT/Ms1WmSLnEAisEXszjzu/s+ce6HZB2VHA==",
"requires": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
}
},
"formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"requires": {
"fetch-blob": "^3.1.2"
}
},
"http-auth-utils": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/http-auth-utils/-/http-auth-utils-3.0.2.tgz",
"integrity": "sha512-cQ8957aiUX0lgV1620uIGKGJc0sEuD/QK4ueZ0hb60MGbO0f6ahcuIgPjamAD98D/AUGizKVm+dNvUVHs0f4Ow==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/http-auth-utils/-/http-auth-utils-5.0.1.tgz",
"integrity": "sha512-YPiLVYdwpBEWB85iWYg7V/ZW3mBfPLCTFQWEiPAA5CKXHJOAPbnJ0xDHLiE6KbPRvrYCwLuWqTG0fLfXVjMUcQ==",
"requires": {
"yerror": "^6.0.0"
"yerror": "^8.0.0"
}
},
"make-error": {
@@ -672,20 +579,6 @@
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="
},
"node-fetch-commonjs": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/node-fetch-commonjs/-/node-fetch-commonjs-3.1.1.tgz",
"integrity": "sha512-TgkdVJdiEaauzWwB9NoD4TvHZFtG6KKEffvotWf9WNIyoRZHsCFjGfb3bhkIXrMt3YFgFi8ZApbwWoe1h3XTpA==",
"requires": {
"formdata-polyfill": "^4.0.10",
"web-streams-polyfill": "^3.1.1"
}
},
"ts-node": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
@@ -724,15 +617,10 @@
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"dev": true
},
"web-streams-polyfill": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz",
"integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA=="
},
"yerror": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/yerror/-/yerror-6.2.1.tgz",
"integrity": "sha512-WPZgybhCBzsMSSqGYBnj20NZo4FiFKQG0+i/21cYGVd4B7eYtvYDOpjk/0e8UM1eVHJ+4Ja6bZ7TAjHH6mk+ew=="
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/yerror/-/yerror-8.0.0.tgz",
"integrity": "sha512-FemWD5/UqNm8ffj8oZIbjWXIF2KE0mZssggYpdaQkWDDgXBQ/35PNIxEuz6/YLn9o0kOxDBNJe8x8k9ljD7k/g=="
},
"yn": {
"version": "3.1.1",

View File

@@ -13,12 +13,11 @@
"dependencies": {
"@scrypted/sdk": "file:../sdk",
"@scrypted/server": "file:../server",
"http-auth-utils": "^3.0.2",
"node-fetch-commonjs": "^3.1.1",
"http-auth-utils": "^5.0.1",
"typescript": "^5.3.3"
},
"devDependencies": {
"@types/node": "^20.10.8",
"@types/node": "^20.11.0",
"ts-node": "^10.9.2"
}
}

View File

@@ -1,12 +1,9 @@
import type { TranspileOptions } from "typescript";
import sdk, { ScryptedDeviceBase, MixinDeviceBase, ScryptedInterface, ScryptedDeviceType } from "@scrypted/sdk";
import vm from "vm";
import sdk, { MixinDeviceBase, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedInterfaceDescriptors } from "@scrypted/sdk";
import fs from 'fs';
import type { TranspileOptions } from "typescript";
import vm from "vm";
import { ScriptDevice } from "./monaco/script-device";
import { ScryptedInterfaceDescriptors } from "@scrypted/sdk";
import fetch from 'node-fetch-commonjs';
import { PluginAPIProxy } from '../../../server/src/plugin/plugin-api';
import { SystemManagerImpl } from '../../../server/src/plugin/system';
import path from 'path';
const { systemManager, deviceManager, mediaManager, endpointManager } = sdk;
@@ -26,9 +23,13 @@ export async function tsCompile(source: string, options: TranspileOptions = null
return ts.transpileModule(source, options).outputText;
}
export function readFileAsString(f: string) {
return fs.readFileSync(f).toString();;
}
function getTypeDefs() {
const scryptedTypesDefs = fs.readFileSync('@types/sdk/types.d.ts').toString();
const scryptedIndexDefs = fs.readFileSync('@types/sdk/index.d.ts').toString();
const scryptedTypesDefs = readFileAsString('@types/sdk/types.d.ts');
const scryptedIndexDefs = readFileAsString('@types/sdk/index.d.ts');
return {
scryptedIndexDefs,
scryptedTypesDefs,
@@ -61,7 +62,6 @@ export async function scryptedEval(device: ScryptedDeviceBase, script: string, e
const allParams = Object.assign({}, params, {
sdk,
fs: require('realfs'),
fetch,
ScryptedDeviceBase,
MixinDeviceBase,
systemManager,
@@ -109,7 +109,7 @@ export async function scryptedEval(device: ScryptedDeviceBase, script: string, e
}
export function createMonacoEvalDefaults(extraLibs: { [lib: string]: string }) {
const bufferTypeDefs = fs.readFileSync('@types/node/buffer.d.ts').toString();
const bufferTypeDefs= readFileAsString('@types/node/buffer.d.ts');
const safeLibs = {
bufferTypeDefs,

View File

@@ -1,117 +1,12 @@
import { HttpFetchOptions, HttpFetchResponseType, checkStatus, getHttpFetchParser, httpFetch } from '@scrypted/server/src/http-fetch-helpers';
import crypto from 'crypto';
import { IncomingMessage } from 'http';
import { BASIC, DIGEST, buildAuthorizationHeader, parseWWWAuthenticateHeader } from 'http-auth-utils/src/index';
import { httpFetch, httpFetchParseIncomingMessage } from '../../server/src/fetch/http-fetch';
import type { IncomingMessage } from 'http';
import type { Readable } from 'stream';
import { createAuthFetch } from '../../packages/auth-fetch/src/auth-fetch';
export interface AuthFetchCredentialState {
username: string;
password: string;
count?: number;
digest?: ReturnType<typeof parseWWWAuthenticateHeader<typeof DIGEST>>;
basic?: ReturnType<typeof parseWWWAuthenticateHeader<typeof BASIC>>;
}
export type { HttpFetchOptions, HttpFetchResponseType } from '../../server/src/fetch/http-fetch';
export type { AuthFetchCredentialState, AuthFetchOptions } from '../../packages/auth-fetch/src/auth-fetch';
export interface AuthFetchOptions<T extends HttpFetchResponseType> extends HttpFetchOptions<T> {
credential: AuthFetchCredentialState;
}
function getAuth(options: AuthFetchOptions<any>, method: string) {
if (!options.credential)
return;
const { digest, basic } = options.credential;
if (digest) {
options.credential.count ||= 0;
++options.credential.count;
const nc = ('00000000' + options.credential.count).slice(-8);
const cnonce = crypto.randomBytes(24).toString('hex');
const uri = new URL(options.url).pathname;
const response = DIGEST.computeHash({
username: options.credential.username,
password: options.credential.password,
method,
uri,
nc,
cnonce,
algorithm: 'MD5',
qop: digest.data.qop!,
...digest.data,
});
const header = buildAuthorizationHeader(DIGEST, {
username: options.credential.username,
uri,
nc,
cnonce,
algorithm: digest.data.algorithm!,
qop: digest.data.qop!,
response,
...digest.data,
});
return header;
}
else if (basic) {
const header = buildAuthorizationHeader(BASIC, {
username: options.credential.username,
password: options.credential.password,
});
return header;
}
}
export async function authHttpFetch<T extends HttpFetchResponseType>(options: AuthFetchOptions<T>): ReturnType<typeof httpFetch<AuthFetchOptions<T>>> {
const method = options.method || 'GET';
const headers = new Headers(options.headers);
options.headers = headers;
const initialHeader = getAuth(options, method);
if (initialHeader)
headers.set('Authorization', initialHeader);
const initialResponse = await httpFetch({
...options,
ignoreStatusCode: true,
responseType: 'readable',
});
const parser = getHttpFetchParser(options.responseType);
if (initialResponse.statusCode !== 401 || !options.credential) {
if (!options?.ignoreStatusCode)
checkStatus(initialResponse.statusCode);
return {
...initialResponse,
body: await parser.parse(initialResponse.body),
};
}
let authenticateHeaders: string | string[] = initialResponse.headers['www-authenticate'];
if (!authenticateHeaders)
throw new Error('Did not find WWW-Authenticate header.');
if (typeof authenticateHeaders === 'string')
authenticateHeaders = [authenticateHeaders];
const parsedHeaders = authenticateHeaders.map(h => parseWWWAuthenticateHeader(h));
const digest = parsedHeaders.find(p => p.type === 'Digest') as ReturnType<typeof parseWWWAuthenticateHeader<typeof DIGEST>>;
const basic = parsedHeaders.find(p => p.type === 'Basic') as ReturnType<typeof parseWWWAuthenticateHeader<typeof BASIC>>;
options.credential.digest = digest;
options.credential.basic = basic;
if (!digest && !basic)
throw new Error(`Unknown WWW-Authenticate type: ${parsedHeaders[0]?.type}`);
const header = getAuth(options, method);
if (header)
headers.set('Authorization', header);
return httpFetch(options);
}
export const authHttpFetch = createAuthFetch<Readable, IncomingMessage>(httpFetch, httpFetchParseIncomingMessage);
function ensureType<T>(v: T) {
}
@@ -144,5 +39,12 @@ async function test() {
responseType: 'buffer',
});
ensureType<Buffer>(d.body);
const e = await authHttpFetch({
credential: undefined,
url: 'http://example.com',
responseType: 'text',
});
ensureType<string>(e.body);
}

View File

@@ -1,8 +1,6 @@
import crypto, { randomBytes } from 'crypto';
import dgram from 'dgram';
import { once } from 'events';
import { BASIC } from 'http-auth-utils/dist/index';
import { parseHTTPHeadersQuotedKeyValueSet } from 'http-auth-utils/dist/utils';
import net from 'net';
import { Duplex, Readable, Writable } from 'stream';
import tls from 'tls';
@@ -363,7 +361,7 @@ export class RtspClient extends RtspBase {
}
}
writeRequest(method: string, headers?: Headers, path?: string, body?: Buffer) {
async writeRequest(method: string, headers?: Headers, path?: string, body?: Buffer) {
headers = headers || {};
let fullUrl = this.url;
@@ -390,7 +388,7 @@ export class RtspClient extends RtspBase {
headers['User-Agent'] = 'Scrypted';
if (this.wwwAuthenticate)
headers['Authorization'] = this.createAuthorizationHeader(method, new URL(fullUrl));
headers['Authorization'] = await this.createAuthorizationHeader(method, new URL(fullUrl));
if (this.session)
headers['Session'] = this.session;
@@ -531,10 +529,13 @@ export class RtspClient extends RtspBase {
}
}
createAuthorizationHeader(method: string, url: URL) {
async createAuthorizationHeader(method: string, url: URL) {
if (!this.wwwAuthenticate)
throw new Error('no WWW-Authenticate found');
const { BASIC } = await import('http-auth-utils');
const { parseHTTPHeadersQuotedKeyValueSet } = await import('http-auth-utils/dist/utils');
if (this.wwwAuthenticate.includes('Basic')) {
const hash = BASIC.computeHash(url);
return `Basic ${hash}`;
@@ -586,7 +587,7 @@ export class RtspClient extends RtspBase {
}
async request(method: string, headers?: Headers, path?: string, body?: Buffer, authenticating?: boolean): Promise<RtspServerResponse> {
this.writeRequest(method, headers, path, body);
await this.writeRequest(method, headers, path, body);
const message = this.requestTimeout ? await timeoutPromise(this.requestTimeout, this.readMessage()) : await this.readMessage();
const statusLine = message[0];

View File

@@ -1,6 +1,6 @@
# Home Assistant Addon Configuration
name: Scrypted
version: "18-jammy-full.s6-v0.80.0"
version: "18-jammy-full.s6-v0.91.6"
slug: scrypted
description: Scrypted is a high performance home video integration and automation platform
url: "https://github.com/koush/scrypted"

View File

@@ -16,6 +16,6 @@ ENV NODE_OPTIONS="--dns-result-order=ipv4first"
# changing this forces pip and npm to perform reinstalls.
# if this base image changes, this version must be updated.
ENV SCRYPTED_BASE_VERSION="20240103"
ENV SCRYPTED_BASE_VERSION="20241303"
CMD npm --prefix /server exec scrypted-serve

View File

@@ -6,13 +6,7 @@ ENV DEBIAN_FRONTEND=noninteractive
# base tools and development stuff
RUN apt-get update && apt-get -y install \
curl software-properties-common apt-utils \
build-essential \
cmake \
ffmpeg \
gcc \
libcairo2-dev \
libgirepository1.0-dev \
pkg-config && \
ffmpeg && \
apt-get -y update && \
apt-get -y upgrade

View File

@@ -46,6 +46,6 @@ ENV NODE_OPTIONS="--dns-result-order=ipv4first"
# changing this forces pip and npm to perform reinstalls.
# if this base image changes, this version must be updated.
ENV SCRYPTED_BASE_VERSION="20240103"
ENV SCRYPTED_BASE_VERSION="20241303"
CMD npm --prefix /server exec scrypted-serve

View File

@@ -26,6 +26,10 @@ then
fi
fi
echo "Stopping local service if it is running..."
systemctl stop scrypted.service 2> /dev/null
systemctl disable scrypted.service 2> /dev/null
USER_HOME=$(eval echo ~$SERVICE_USER)
SCRYPTED_HOME=$USER_HOME/.scrypted
mkdir -p $SCRYPTED_HOME
@@ -48,21 +52,12 @@ echo "Created $DOCKER_COMPOSE_YML"
curl -s https://raw.githubusercontent.com/koush/scrypted/main/install/docker/docker-compose.yml | sed s/SET_THIS_TO_SOME_RANDOM_TEXT/"$(echo $RANDOM | md5sum | head -c 32)"/g > $DOCKER_COMPOSE_YML
if [ -d /dev/dri ]
then
sed -i 's/'#' - \/dev\/dri/- \/dev\/dri/g' $DOCKER_COMPOSE_YML
sed -i 's/'#' "\/dev\/dri/"\/dev\/dri/g' $DOCKER_COMPOSE_YML
fi
echo "Setting permissions on $SCRYPTED_HOME"
chown -R $SERVICE_USER $SCRYPTED_HOME
echo "Optional:"
readyn "Edit docker-compose.yml to add external storage for Scrypted NVR?"
if [ "$yn" == "y" ]
then
apt install nano
nano $DOCKER_COMPOSE_YML
fi
set +e
echo "docker compose down"
@@ -85,3 +80,6 @@ echo "Scrypted is now running at: https://localhost:10443/"
echo "Note that it is https and that you'll be asked to approve/ignore the website certificate."
echo
echo
echo "Optional:"
echo "Scrypted NVR Recording storage directory can be configured with an additional script:"
echo "https://docs.scrypted.app/scrypted-nvr/installation.html#docker-volume"

View File

@@ -0,0 +1,140 @@
if [ -z "$SERVICE_USER" ]
then
echo "Scrypted SERVICE_USER environment variable was not specified. NVR Storage can not be configured."
exit 0
fi
if [ "$USER" != "root" ]
then
echo "$USER"
echo "This script must be run as sudo or root."
exit 1
fi
USER_HOME=$(eval echo ~$SERVICE_USER)
SCRYPTED_HOME=$USER_HOME/.scrypted
DOCKER_COMPOSE_YML=$SCRYPTED_HOME/docker-compose.yml
if [ ! -f "$DOCKER_COMPOSE_YML" ]
then
echo "$DOCKER_COMPOSE_YML not found. Install Scrypted first."
exit 1
fi
NVR_MOUNT_LINE=$(cat "$DOCKER_COMPOSE_YML" | grep :/nvr)
if [ -z "$NVR_MOUNT_LINE" ]
then
echo "Unexpected contents in $DOCKER_COMPOSE_YML. Rerun the Scrypted docker compose installer."
exit 1
fi
function backup() {
BACKUP_FILE="$1".scrypted-bak
if [ ! -f "$BACKUP_FILE" ]
then
cp "$1" "$BACKUP_FILE"
fi
}
backup "$DOCKER_COMPOSE_YML"
function readyn() {
while true; do
read -p "$1 (y/n) " yn
case $yn in
[Yy]* ) break;;
[Nn]* ) break;;
* ) echo "Please answer yes or no. (y/n)";;
esac
done
}
if [ -z "$1" ]
then
lsblk
echo ""
echo "Please run the script with an existing mount path or the 'disk' device to format (e.g. sdx)."
exit 1
fi
function stopscrypted() {
cd "$SCRYPTED_HOME"
echo ""
echo "Stopping the Scrypted container. If there are any errors during disk setup, Scrypted will need to be manually restarted with:"
echo "cd $SCRYPTED_HOME && docker compose up -d"
echo ""
docker compose down
}
BLOCK_DEVICE="/dev/$1"
if [ -b "$BLOCK_DEVICE" ]
then
readyn "Format $BLOCK_DEVICE?"
if [ "$yn" == "n" ]
then
exit 1
fi
stopscrypted
umount "$BLOCK_DEVICE"1 2> /dev/null
umount "$BLOCK_DEVICE"2 2> /dev/null
umount /mnt/scrypted-nvr 2> /dev/null
set -e
parted "$BLOCK_DEVICE" --script mklabel gpt
parted -a optimal "$BLOCK_DEVICE" mkpart scrypted-nvr "0%" "100%"
set +e
sync
mkfs -F -t ext4 "$BLOCK_DEVICE"1
sync
# parse/evaluate blkid line as env vars
for attr in $(blkid | grep "$BLOCK_DEVICE")
do
e=$(echo $attr | grep =)
if [ ! -z "$e" ]
then
export "$e"
fi
done
if [ -z "$UUID" ]
then
echo "Error parsing disk UUID."
exit 1
fi
echo "UUID: $UUID"
set -e
backup "/etc/fstab"
grep -v "scrypted-nvr" /etc/fstab > /tmp/fstab && cp /tmp/fstab /etc/fstab
# ensure newline
sed -i -e '$a\' /etc/fstab
mkdir -p /mnt/scrypted-nvr
echo "PARTLABEL=scrypted-nvr /mnt/scrypted-nvr ext4 defaults 0 0" >> /etc/fstab
mount -a
set +e
DIR="/mnt/scrypted-nvr"
else
if [ ! -d "$1" ]
then
echo "$1 is not a valid directory."
exit 1
fi
stopscrypted
DIR="$1"
fi
ESCAPED_DIR=$(echo "$DIR" | sed s/\\//\\\\\\//g)
set -e
sed -i s/'^.*:\/nvr'/" - $ESCAPED_DIR:\/nvr"/ "$DOCKER_COMPOSE_YML"
sed -i s/'^.*SCRYPTED_NVR_VOLUME.*$'/" - SCRYPTED_NVR_VOLUME=\/nvr"/ "$DOCKER_COMPOSE_YML"
set +e
cd "$SCRYPTED_HOME" && docker compose up -d

View File

@@ -89,6 +89,13 @@ USER_HOME=$(eval echo ~$SERVICE_USER)
echo "Setting permissions on $USER_HOME/.scrypted"
chown -R $SERVICE_USER $USER_HOME/.scrypted
echo "Stopping docker service if it exists..."
cd $USER_HOME/.scrypted
echo "docker compose down"
sudo -u $SERVICE_USER docker compose down 2> /dev/null
echo "docker compose rm -rf"
sudo -u $SERVICE_USER docker rm -f /scrypted /scrypted-watchtower 2> /dev/null
echo "Installing Scrypted..."
RUN sudo -u $SERVICE_USER npx -y scrypted@latest install-server

View File

@@ -51,6 +51,15 @@ then
exit 1
fi
pct set $VMID -net0 name=eth0,bridge=vmbr0,ip=dhcp,ip6=auto
if [ "$?" != "0" ]
then
echo ""
echo "pct set network failed"
echo ""
echo "Ignoring... Please verify your container's network settings."
fi
CONF=/etc/pve/lxc/$VMID.conf
if [ -f "$CONF" ]
then

View File

@@ -14,7 +14,7 @@ cd $(dirname $0)
git submodule init
git submodule update
for directory in sdk common server packages/client
for directory in sdk common server packages/client packages/auth-fetch
do
echo "$directory > npm install"
pushd $directory

11
packages/auth-fetch/.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
node_modules
.DS_Store
.gcloud/
dist/
volume
scrypted.db
out
scrypted.db.bak
.exit
.update
.venv

580
packages/auth-fetch/package-lock.json generated Normal file
View File

@@ -0,0 +1,580 @@
{
"name": "@scrypted/auth-fetch",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@scrypted/auth-fetch",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"follow-redirects": "^1.15.4",
"http-auth-utils": "^5.0.1"
},
"devDependencies": {
"@types/node": "^20.9.4",
"rimraf": "^5.0.5",
"typescript": "^5.3.2"
}
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
"dev": true,
"dependencies": {
"string-width": "^5.1.2",
"string-width-cjs": "npm:string-width@^4.2.0",
"strip-ansi": "^7.0.1",
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
"wrap-ansi": "^8.1.0",
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
"dev": true,
"optional": true,
"engines": {
"node": ">=14"
}
},
"node_modules/@types/node": {
"version": "20.10.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.8.tgz",
"integrity": "sha512-f8nQs3cLxbAFc00vEU59yf9UyGUftkPaLGfvbVOIDdx2i1b8epBqj2aNGyP19fiyXWvlmZ7qC1XLjAzw/OKIeA==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/ansi-regex": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"node_modules/ansi-styles": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"dev": true,
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"dev": true
},
"node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/foreground-child": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
"integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
"dev": true,
"dependencies": {
"cross-spawn": "^7.0.0",
"signal-exit": "^4.0.1"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/glob": {
"version": "10.3.10",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
"integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
"dev": true,
"dependencies": {
"foreground-child": "^3.1.0",
"jackspeak": "^2.3.5",
"minimatch": "^9.0.1",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
"path-scurry": "^1.10.1"
},
"bin": {
"glob": "dist/esm/bin.mjs"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/http-auth-utils": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/http-auth-utils/-/http-auth-utils-5.0.1.tgz",
"integrity": "sha512-YPiLVYdwpBEWB85iWYg7V/ZW3mBfPLCTFQWEiPAA5CKXHJOAPbnJ0xDHLiE6KbPRvrYCwLuWqTG0fLfXVjMUcQ==",
"dependencies": {
"yerror": "^8.0.0"
},
"engines": {
"node": ">=18.16.0"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true
},
"node_modules/jackspeak": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
"integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
"dev": true,
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
},
"optionalDependencies": {
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/lru-cache": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz",
"integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==",
"dev": true,
"engines": {
"node": "14 || >=16.14"
}
},
"node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/minipass": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
"integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
"dev": true,
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/path-scurry": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
"integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
"dev": true,
"dependencies": {
"lru-cache": "^9.1.1 || ^10.0.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/rimraf": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
"integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
"dev": true,
"dependencies": {
"glob": "^10.3.7"
},
"bin": {
"rimraf": "dist/esm/bin.mjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"dependencies": {
"shebang-regex": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"dev": true,
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/string-width": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"dev": true,
"dependencies": {
"eastasianwidth": "^0.2.0",
"emoji-regex": "^9.2.2",
"strip-ansi": "^7.0.1"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/string-width-cjs": {
"name": "string-width",
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/string-width-cjs/node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/string-width-cjs/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
"node_modules/string-width-cjs/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"dependencies": {
"ansi-regex": "^6.0.1"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
"node_modules/strip-ansi-cjs": {
"name": "strip-ansi",
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/typescript": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"node-which": "bin/node-which"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/wrap-ansi": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
"dev": true,
"dependencies": {
"ansi-styles": "^6.1.0",
"string-width": "^5.0.1",
"strip-ansi": "^7.0.1"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrap-ansi-cjs": {
"name": "wrap-ansi",
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yerror": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/yerror/-/yerror-8.0.0.tgz",
"integrity": "sha512-FemWD5/UqNm8ffj8oZIbjWXIF2KE0mZssggYpdaQkWDDgXBQ/35PNIxEuz6/YLn9o0kOxDBNJe8x8k9ljD7k/g==",
"engines": {
"node": ">=18.16.0"
}
}
}
}

View File

@@ -0,0 +1,23 @@
{
"name": "@scrypted/auth-fetch",
"version": "1.0.0",
"description": "",
"main": "dist/index.js",
"scripts": {
"prebuild": "rimraf dist",
"build": "tsc --outDir dist",
"prepublishOnly": "npm run build",
"test": "echo \"Error: no test specified\" && exit 1"
},
"devDependencies": {
"@types/node": "^20.9.4",
"rimraf": "^5.0.5",
"typescript": "^5.3.2"
},
"dependencies": {
"follow-redirects": "^1.15.4",
"http-auth-utils": "^5.0.1"
},
"author": "",
"license": "ISC"
}

View File

@@ -0,0 +1,135 @@
import { HttpFetchOptions, HttpFetchResponseType, checkStatus, fetcher, getFetchMethod, setDefaultHttpFetchAccept } from '../../../server/src/fetch';
export interface AuthFetchCredentialState {
username: string;
password: string;
}
export interface AuthFetchOptions {
credential?: AuthFetchCredentialState;
}
async function getAuth(options: AuthFetchOptions, url: string | URL, method: string) {
if (!options.credential)
return;
const { BASIC, DIGEST, parseWWWAuthenticateHeader } = await import('http-auth-utils');
const credential = options.credential as AuthFetchCredentialState & {
count?: number;
digest?: ReturnType<typeof parseWWWAuthenticateHeader<typeof DIGEST>>;
basic?: ReturnType<typeof parseWWWAuthenticateHeader<typeof BASIC>>;
};
const { digest, basic } = credential;
if (digest) {
credential.count ||= 0;
++credential.count;
const nc = ('00000000' + credential.count).slice(-8);
const cnonce = [...Array(24)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
const uri = new URL(url).pathname;
const { DIGEST, buildAuthorizationHeader } = await import('http-auth-utils');
const response = DIGEST.computeHash({
username: options.credential.username,
password: options.credential.password,
method,
uri,
nc,
cnonce,
algorithm: 'MD5',
qop: digest.data.qop!,
...digest.data,
});
const header = buildAuthorizationHeader(DIGEST, {
username: options.credential.username,
uri,
nc,
cnonce,
algorithm: digest.data.algorithm!,
qop: digest.data.qop!,
response,
...digest.data,
});
return header;
}
else if (basic) {
const { BASIC, buildAuthorizationHeader } = await import('http-auth-utils');
const header = buildAuthorizationHeader(BASIC, {
username: options.credential.username,
password: options.credential.password,
});
return header;
}
}
export function createAuthFetch<B, M>(
h: fetcher<B, M>,
parser: (body: M, responseType: HttpFetchResponseType) => Promise<any>
) {
const authHttpFetch = async <T extends HttpFetchOptions<B>>(options: T & AuthFetchOptions): ReturnType<typeof h<T>> => {
const method = getFetchMethod(options);
const headers = new Headers(options.headers);
options.headers = headers;
setDefaultHttpFetchAccept(headers, options.responseType);
const initialHeader = await getAuth(options, options.url, method);
// try to provide an authorization if a session exists, but don't override Authorization if provided already.
// 401 will trigger a proper auth.
if (initialHeader && !headers.has('Authorization'))
headers.set('Authorization', initialHeader);
const initialResponse = await h({
...options,
ignoreStatusCode: true,
responseType: 'readable',
});
if (initialResponse.statusCode !== 401 || !options.credential) {
if (!options?.ignoreStatusCode)
checkStatus(initialResponse.statusCode);
return {
...initialResponse,
body: await parser(initialResponse.body, options.responseType),
};
}
let authenticateHeaders: string | string[] = initialResponse.headers.get('www-authenticate');
if (!authenticateHeaders)
throw new Error('Did not find WWW-Authenticate header.');
if (typeof authenticateHeaders === 'string')
authenticateHeaders = [authenticateHeaders];
const { BASIC, DIGEST, parseWWWAuthenticateHeader } = await import('http-auth-utils');
const parsedHeaders = authenticateHeaders.map(h => parseWWWAuthenticateHeader(h));
const digest = parsedHeaders.find(p => p.type === 'Digest') as ReturnType<typeof parseWWWAuthenticateHeader<typeof DIGEST>>;
const basic = parsedHeaders.find(p => p.type === 'Basic') as ReturnType<typeof parseWWWAuthenticateHeader<typeof BASIC>>;
const credential = options.credential as AuthFetchCredentialState & {
count?: number;
digest?: ReturnType<typeof parseWWWAuthenticateHeader<typeof DIGEST>>;
basic?: ReturnType<typeof parseWWWAuthenticateHeader<typeof BASIC>>;
};
credential.digest = digest;
credential.basic = basic;
if (!digest && !basic)
throw new Error(`Unknown WWW-Authenticate type: ${parsedHeaders[0]?.type}`);
const header = await getAuth(options, options.url, method);
if (header)
headers.set('Authorization', header);
return h(options);
}
return authHttpFetch;
}

View File

@@ -0,0 +1,19 @@
import { createAuthFetch } from "./auth-fetch";
import { httpFetch, httpFetchParseIncomingMessage } from '../../../server/src/fetch/http-fetch';
import type { Readable } from "stream";
import type { IncomingMessage } from "http";
import { domFetch, domFetchParseIncomingMessage } from "../../../server/src/fetch";
function init() {
try {
require('net');
require('events');
return createAuthFetch<Readable, IncomingMessage>(httpFetch, httpFetchParseIncomingMessage);
}
catch (e) {
}
return createAuthFetch<BodyInit, Response>(domFetch, domFetchParseIncomingMessage);
}
export const authFetch = init();

View File

@@ -0,0 +1,16 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "esnext",
"noImplicitAny": true,
"outDir": "./dist",
"esModuleInterop": true,
"sourceMap": true,
"inlineSources": true,
"declaration": true,
"resolveJsonModule": true,
},
"include": [
"src/**/*"
],
}

View File

@@ -21,7 +21,7 @@
],
"preLaunchTask": "npm: build",
"args": [
"shell",
"login",
],
"sourceMaps": true,
"resolveSourceMapLocations": [

View File

@@ -1,17 +1,16 @@
{
"name": "scrypted",
"version": "1.3.4",
"version": "1.3.10",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "scrypted",
"version": "1.3.4",
"version": "1.3.10",
"license": "ISC",
"dependencies": {
"@scrypted/client": "^1.3.2",
"@scrypted/client": "^1.3.3",
"@scrypted/types": "^0.2.99",
"axios": "^0.25.0",
"engine.io-client": "^6.5.3",
"readline-sync": "^1.4.10",
"semver": "^7.5.4",
@@ -92,16 +91,21 @@
}
},
"node_modules/@scrypted/client": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@scrypted/client/-/client-1.3.2.tgz",
"integrity": "sha512-PZwjfKUYIMxBYm7V2o0/vAMlQmznKLN/d4rpshb5vV086mnhh578ik3h39awkwoPyWzNGDcYeoBY0BchhwtdOQ==",
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@scrypted/client/-/client-1.3.3.tgz",
"integrity": "sha512-Wuy7x02TCRy1buaDNX8NOIaL1j4ZXu4dqTTJsKHlPe3+umsBvpwbylD+YyyU8ghQJC6a40Bs5UMsvnCvNa/1fg==",
"dependencies": {
"@scrypted/types": "^0.2.99",
"axios": "^0.25.0",
"@scrypted/types": "^0.3.4",
"engine.io-client": "^6.5.3",
"follow-redirects": "^1.15.4",
"rimraf": "^5.0.5"
}
},
"node_modules/@scrypted/client/node_modules/@scrypted/types": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.3.4.tgz",
"integrity": "sha512-k/YMx8lIWOkePgXfKW9POr12mb+erFU2JKxO7TW92GyW8ojUWw9VOc0PK6O9bybi0vhsEnvMFkO6pO6bAonsVA=="
},
"node_modules/@scrypted/types": {
"version": "0.2.99",
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.2.99.tgz",
@@ -206,14 +210,6 @@
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
"node_modules/axios": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz",
"integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==",
"dependencies": {
"follow-redirects": "^1.14.7"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -318,9 +314,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"funding": [
{
"type": "individual",

View File

@@ -1,6 +1,6 @@
{
"name": "scrypted",
"version": "1.3.5",
"version": "1.3.10",
"description": "",
"main": "./dist/packages/cli/src/main.js",
"bin": {
@@ -16,19 +16,18 @@
"author": "",
"license": "ISC",
"dependencies": {
"@scrypted/client": "^1.3.2",
"@scrypted/client": "^1.3.3",
"@scrypted/types": "^0.2.99",
"axios": "^0.25.0",
"engine.io-client": "^6.5.3",
"readline-sync": "^1.4.10",
"semver": "^7.5.4",
"tslib": "^2.6.2"
},
"devDependencies": {
"rimraf": "^5.0.5",
"@types/node": "^20.9.4",
"@types/readline-sync": "^1.4.8",
"@types/semver": "^7.5.6",
"rimraf": "^5.0.5",
"ts-node": "^10.9.1",
"typescript": "^5.3.2"
}

View File

@@ -2,20 +2,15 @@
import { connectScryptedClient } from '@scrypted/client';
import { FFmpegInput, ScryptedMimeTypes } from '@scrypted/types';
import axios, { AxiosRequestConfig } from 'axios';
import child_process from 'child_process';
import fs from 'fs';
import https from 'https';
import path from 'path';
import readline from 'readline-sync';
import semver from 'semver';
import { httpFetch } from '../../../server/src/fetch/http-fetch';
import { installServe, serveMain } from './service';
import { connectShell } from './shell';
const httpsAgent = new https.Agent({
rejectUnauthorized: false,
});
if (!semver.gte(process.version, '16.0.0')) {
throw new Error('"node" version out of date. Please update node to v16 or higher.')
}
@@ -45,6 +40,12 @@ interface LoginFile {
[host: string]: Login;
}
function basicAuthHeaders(username: string, password: string) {
const headers = new Headers();
headers.set('Authorization', `Basic ${Buffer.from(username + ":" + password).toString('base64')}`);
return headers;
}
async function doLogin(host: string) {
host = toIpAndPort(host);
@@ -54,15 +55,15 @@ async function doLogin(host: string) {
});
const url = `https://${host}/login`;
const response = await axios(Object.assign({
const response = await httpFetch({
method: 'GET',
auth: {
username,
password,
},
headers: basicAuthHeaders(username, password),
url,
httpsAgent,
}, axiosConfig));
rejectUnauthorized: false,
responseType: 'json',
});
if (response.body.error)
throw new Error(response.body.error);
fs.mkdirSync(scryptedHome, {
recursive: true,
@@ -78,15 +79,12 @@ async function doLogin(host: string) {
login = {};
login = login || {};
login[host] = response.data;
login[host] = response.body;
fs.writeFileSync(loginPath, JSON.stringify(login));
return login;
return login[host];
}
async function getOrDoLogin(host: string): Promise<{
username: string,
token: string,
}> {
async function getOrDoLogin(host: string): Promise<Login> {
let login: LoginFile;
try {
login = JSON.parse(fs.readFileSync(loginPath).toString());
@@ -95,17 +93,12 @@ async function getOrDoLogin(host: string): Promise<{
if (!login[host].username || !login[host].token)
throw new Error();
return login[host];
}
catch (e) {
login = await doLogin(host);
return doLogin(host);
}
return login[host];
}
const axiosConfig: AxiosRequestConfig = {
httpsAgent: new https.Agent({
rejectUnauthorized: false
})
}
async function runCommand() {
@@ -119,9 +112,6 @@ async function runCommand() {
pluginId: '@scrypted/core',
username: login.username,
password: login.token,
axiosConfig: {
httpsAgent,
}
});
const device: any = sdk.systemManager.getDeviceById(idOrName) || sdk.systemManager.getDeviceByName(idOrName);
@@ -158,8 +148,8 @@ async function main() {
}
else if (process.argv[2] === 'login') {
const ip = process.argv[3] || '127.0.0.1';
const token = await doLogin(ip);
console.log('login successful. token:', token);
const login = await doLogin(ip);
console.log('login successful. token:', login.token);
}
else if (process.argv[2] === 'command') {
const { sdk, pendingResult } = await runCommand();
@@ -211,16 +201,17 @@ async function main() {
const login = await getOrDoLogin(ip);
const url = `https://${ip}/web/component/script/install/${pkg}`;
const response = await axios(Object.assign({
const response = await httpFetch({
method: 'POST',
auth: {
username: login.username,
password: login.token,
},
headers: basicAuthHeaders(login.username, login.token),
url,
}, axiosConfig));
rejectUnauthorized: false,
responseType: 'json',
});
if (response.body.error)
throw new Error(response.body.error);
console.log('install successful. id:', response.data.id);
console.log('install successful. id:', response.body.id);
}
else if (process.argv[2] === 'shell') {
console.log = () => { };
@@ -232,9 +223,6 @@ async function main() {
pluginId: '@scrypted/core',
username: login.username,
password: login.token,
axiosConfig: {
httpsAgent,
}
});
const separator = process.argv.indexOf("--");
@@ -258,7 +246,6 @@ async function main() {
console.log(' npx scrypted install @scrypted/rtsp');
console.log(' npx scrypted install @scrypted/rtsp/0.0.51');
console.log(' npx scrypted install @scrypted/rtsp/0.0.51 192.168.2.100');
process.exit(1);
}
}

View File

@@ -1,6 +1,6 @@
{
"compilerOptions": {
"module": "commonjs",
"module": "Node16",
"target": "esnext",
"noImplicitAny": true,
"outDir": "./dist",
@@ -8,7 +8,7 @@
"sourceMap": true,
"inlineSources": true,
"declaration": true,
"resolveJsonModule": true,
"moduleResolution": "Node16",
},
"include": [
"src/**/*"

View File

@@ -9,3 +9,4 @@ scrypted.db.bak
.exit
.update
.venv
.vscode/launch.json

View File

@@ -1,27 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ts-node",
"type": "node",
"request": "launch",
"args": [
"${relativeFile}"
],
"runtimeArgs": [
"-r",
"ts-node/register"
],
"env": {
"SCRYPTED_USERNAME": "koush",
"SCRYPTED_PASSWORD": "k9copUSA",
},
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"internalConsoleOptions": "openOnSessionStart"
}
]
}

View File

@@ -1,21 +1,12 @@
import { Camera, VideoCamera, VideoFrameGenerator } from '@scrypted/types';
import { connectScryptedClient } from '../dist/packages/client/src';
import https from 'https';
const httpsAgent = new https.Agent({
rejectUnauthorized: false,
})
async function example() {
const sdk = await connectScryptedClient({
baseUrl: 'https://localhost:10443',
pluginId: "@scrypted/core",
username: process.env.SCRYPTED_USERNAME || 'admin',
password: process.env.SCRYPTED_PASSWORD || 'swordfish',
axiosConfig: {
httpsAgent,
}
});
console.log('server version', sdk.serverVersion);

View File

@@ -1,25 +1,16 @@
import { ObjectDetector, ObjectsDetected, ScryptedInterface } from '@scrypted/types';
import { connectScryptedClient } from '../dist/packages/client/src';
import https from 'https';
const httpsAgent = new https.Agent({
rejectUnauthorized: false,
})
async function example() {
const sdk = await connectScryptedClient({
baseUrl: 'https://localhost:10443',
pluginId: "@scrypted/core",
username: process.env.SCRYPTED_USERNAME || 'admin',
password: process.env.SCRYPTED_PASSWORD || 'swordfish',
axiosConfig: {
httpsAgent,
}
});
console.log('server version', sdk.serverVersion);
const backyard = sdk.systemManager.getDeviceByName<ObjectDetector>("Hikvision Test");
const backyard = sdk.systemManager.getDeviceByName<ObjectDetector>("IP CAMERA");
if (!backyard)
throw new Error('Device not found');

View File

@@ -1,21 +1,12 @@
import { connectScryptedClient } from '../dist/packages/client/src';
import { OnOff } from '@scrypted/types';
import https from 'https';
const httpsAgent = new https.Agent({
rejectUnauthorized: false,
})
async function example() {
const sdk = await connectScryptedClient({
baseUrl: 'https://localhost:10443',
pluginId: "@scrypted/core",
username: process.env.SCRYPTED_USERNAME || 'admin',
password: process.env.SCRYPTED_PASSWORD || 'swordfish',
axiosConfig: {
httpsAgent,
}
});
console.log('server version', sdk.serverVersion);

View File

@@ -1,23 +1,36 @@
{
"name": "@scrypted/client",
"version": "1.3.1",
"version": "1.3.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@scrypted/client",
"version": "1.3.1",
"version": "1.3.4",
"license": "ISC",
"dependencies": {
"@scrypted/types": "^0.2.99",
"axios": "^0.25.0",
"@scrypted/types": "^0.3.4",
"engine.io-client": "^6.5.3",
"follow-redirects": "^1.15.4",
"rimraf": "^5.0.5"
},
"devDependencies": {
"@types/ip": "^1.1.3",
"@types/node": "^20.9.4",
"typescript": "^5.3.2"
"@types/node": "^20.10.8",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
}
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"dev": true,
"dependencies": {
"@jridgewell/trace-mapping": "0.3.9"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@isaacs/cliui": {
@@ -36,6 +49,31 @@
"node": ">=12"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
"dev": true,
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
"dev": true
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -46,15 +84,39 @@
}
},
"node_modules/@scrypted/types": {
"version": "0.2.99",
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.2.99.tgz",
"integrity": "sha512-2J1FH7tpAW5X3rgA70gJ+z0HFM90c/tBA+JXdP1vI1d/0yVmh9TSxnHoCuADN4R2NQXHmoZ6Nbds9kKAQ/25XQ=="
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.3.4.tgz",
"integrity": "sha512-k/YMx8lIWOkePgXfKW9POr12mb+erFU2JKxO7TW92GyW8ojUWw9VOc0PK6O9bybi0vhsEnvMFkO6pO6bAonsVA=="
},
"node_modules/@socket.io/component-emitter": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
},
"node_modules/@tsconfig/node10": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
"dev": true
},
"node_modules/@tsconfig/node12": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
"dev": true
},
"node_modules/@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
"dev": true
},
"node_modules/@tsconfig/node16": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"dev": true
},
"node_modules/@types/ip": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@types/ip/-/ip-1.1.3.tgz",
@@ -65,14 +127,35 @@
}
},
"node_modules/@types/node": {
"version": "20.9.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.4.tgz",
"integrity": "sha512-wmyg8HUhcn6ACjsn8oKYjkN/zUzQeNtMy44weTJSM6p4MMzEOuKbA3OjJ267uPCOW7Xex9dyrNTful8XTQYoDA==",
"version": "20.10.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.8.tgz",
"integrity": "sha512-f8nQs3cLxbAFc00vEU59yf9UyGUftkPaLGfvbVOIDdx2i1b8epBqj2aNGyP19fiyXWvlmZ7qC1XLjAzw/OKIeA==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/acorn": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/acorn-walk": {
"version": "8.3.1",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz",
"integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==",
"dev": true,
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/ansi-regex": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
@@ -95,13 +178,11 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/axios": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz",
"integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==",
"dependencies": {
"follow-redirects": "^1.14.7"
}
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
"node_modules/balanced-match": {
"version": "1.0.2",
@@ -132,6 +213,12 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -161,6 +248,15 @@
}
}
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true,
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@@ -192,9 +288,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"funding": [
{
"type": "individual",
@@ -284,6 +380,12 @@
"node": "14 || >=16.14"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
@@ -469,10 +571,53 @@
"node": ">=8"
}
},
"node_modules/ts-node": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"dev": true,
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-cwd": "dist/bin-cwd.js",
"ts-node-esm": "dist/bin-esm.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"peerDependencies": {
"@swc/core": ">=1.2.50",
"@swc/wasm": ">=1.2.50",
"@types/node": "*",
"typescript": ">=2.7"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
},
"@swc/wasm": {
"optional": true
}
}
},
"node_modules/typescript": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz",
"integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==",
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
@@ -488,6 +633,12 @@
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"dev": true
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -613,6 +764,15 @@
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true,
"engines": {
"node": ">=6"
}
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/client",
"version": "1.3.2",
"version": "1.3.4",
"description": "",
"main": "dist/packages/client/src/index.js",
"scripts": {
@@ -13,13 +13,14 @@
"license": "ISC",
"devDependencies": {
"@types/ip": "^1.1.3",
"@types/node": "^20.9.4",
"typescript": "^5.3.2"
"@types/node": "^20.10.8",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
},
"dependencies": {
"@scrypted/types": "^0.2.99",
"axios": "^0.25.0",
"@scrypted/types": "^0.3.4",
"engine.io-client": "^6.5.3",
"follow-redirects": "^1.15.4",
"rimraf": "^5.0.5"
}
}

View File

@@ -1,20 +1,34 @@
import { MediaObjectOptions, RTCConnectionManagement, RTCSignalingSession, ScryptedStatic } from "@scrypted/types";
import axios, { AxiosRequestConfig, AxiosRequestHeaders } from 'axios';
import * as eio from 'engine.io-client';
import { SocketOptions } from 'engine.io-client';
import { Deferred } from "../../../common/src/deferred";
import { timeoutPromise } from "../../../common/src/promise-utils";
import { BrowserSignalingSession, waitPeerConnectionIceConnected, waitPeerIceConnectionClosed } from "../../../common/src/rtc-signaling";
import { DataChannelDebouncer } from "../../../plugins/webrtc/src/datachannel-debouncer";
import type { ClusterObject, ConnectRPCObject } from '../../../server/src/cluster/connect-rpc-object';
import type { IOSocket } from '../../../server/src/io';
import { MediaObject } from '../../../server/src/plugin/mediaobject';
import { attachPluginRemote } from '../../../server/src/plugin/plugin-remote';
import type { ClusterObject, ConnectRPCObject } from '../../../server/src/cluster/connect-rpc-object';
import { RpcPeer } from '../../../server/src/rpc';
import { createRpcDuplexSerializer, createRpcSerializer } from '../../../server/src/rpc-serializer';
import packageJson from '../package.json';
import { isIPAddress } from "./ip";
import { domFetch } from "../../../server/src/fetch";
import { httpFetch } from '../../../server/src/fetch/http-fetch';
let fetcher: typeof httpFetch | typeof domFetch;
try {
if (process.arch === 'browser' as any)
throw new Error();
require('net');
require('events');
fetcher = httpFetch;
}
catch (e) {
fetcher = domFetch;
}
const sourcePeerId = RpcPeer.generateId();
type IOClientSocket = eio.Socket & IOSocket;
@@ -59,7 +73,6 @@ export interface ScryptedConnectionOptions {
local?: boolean;
webrtc?: boolean;
baseUrl?: string;
axiosConfig?: AxiosRequestConfig;
previousLoginResult?: ScryptedClientLoginResult;
}
@@ -90,10 +103,13 @@ function isRunningStandalone() {
export async function logoutScryptedClient(baseUrl?: string) {
const url = combineBaseUrl(baseUrl, 'logout');
const response = await axios(url, {
const response = await fetcher({
url,
withCredentials: true,
responseType: 'json',
rejectUnauthorized: false,
});
return response.data;
return response.body;
}
export function getCurrentBaseUrl() {
@@ -122,38 +138,43 @@ export async function loginScryptedClient(options: ScryptedLoginOptions) {
maxAge = 365 * 24 * 60 * 60 * 1000;
const url = combineBaseUrl(baseUrl, 'login');
const response = await axios.post(url, {
username,
password,
change_password,
maxAge,
}, {
const response = await fetcher({
url,
body: {
username,
password,
change_password,
maxAge,
},
rejectUnauthorized: false,
withCredentials: true,
...options.axiosConfig,
responseType: 'json',
});
if (response.status !== 200)
throw new Error('status ' + response.status);
if (response.statusCode !== 200)
throw new Error('status ' + response.statusCode);
const { body } = response;
return {
error: response.data.error as string,
authorization: response.data.authorization as string,
queryToken: response.data.queryToken as any,
token: response.data.token as string,
addresses: response.data.addresses as string[],
externalAddresses: response.data.externalAddresses as string[],
error: body.error as string,
authorization: body.authorization as string,
queryToken: body.queryToken as any,
token: body.token as string,
addresses: body.addresses as string[],
externalAddresses: body.externalAddresses as string[],
// the cloud plugin will include this header.
// should maybe move this into the cloud server itself.
scryptedCloud: response.headers['x-scrypted-cloud'] === 'true',
directAddress: response.headers['x-scrypted-direct-address'],
cloudAddress: response.headers['x-scrypted-cloud-address'],
scryptedCloud: response.headers.get('x-scrypted-cloud') === 'true',
directAddress: response.headers.get('x-scrypted-direct-address'),
cloudAddress: response.headers.get('x-scrypted-cloud-address'),
};
}
export async function checkScryptedClientLogin(options?: ScryptedConnectionOptions) {
let { baseUrl } = options || {};
let url = combineBaseUrl(baseUrl, 'login');
const headers: AxiosRequestHeaders = {};
const headers = new Headers();
if (options?.previousLoginResult?.queryToken) {
// headers.Authorization = options?.previousLoginResult?.authorization;
// const search = new URLSearchParams(options.previousLoginResult.queryToken);
@@ -161,32 +182,36 @@ export async function checkScryptedClientLogin(options?: ScryptedConnectionOptio
const token = options?.previousLoginResult.username + ":" + options.previousLoginResult.token;
const hash = Buffer.from(token).toString('base64');
headers.Authorization = `Basic ${hash}`;
headers.set('Authorization', `Basic ${hash}`);
}
const response = await axios.get(url, {
const response = await fetcher({
url,
withCredentials: true,
headers,
...options?.axiosConfig,
rejectUnauthorized: false,
responseType: 'json',
});
const { body } = response;
return {
baseUrl,
hostname: response.data.hostname as string,
redirect: response.data.redirect as string,
username: response.data.username as string,
expiration: response.data.expiration as number,
hasLogin: !!response.data.hasLogin,
error: response.data.error as string,
authorization: response.data.authorization as string,
queryToken: response.data.queryToken as any,
token: response.data.token as string,
addresses: response.data.addresses as string[],
externalAddresses: response.data.externalAddresses as string[],
hostname: body.hostname as string,
redirect: body.redirect as string,
username: body.username as string,
expiration: body.expiration as number,
hasLogin: !!body.hasLogin,
error: body.error as string,
authorization: body.authorization as string,
queryToken: body.queryToken as any,
token: body.token as string,
addresses: body.addresses as string[],
externalAddresses: body.externalAddresses as string[],
// the cloud plugin will include this header.
// should maybe move this into the cloud server itself.
scryptedCloud: response.headers['x-scrypted-cloud'] === 'true',
directAddress: response.headers['x-scrypted-direct-address'],
cloudAddress: response.headers['x-scrypted-cloud-address'],
scryptedCloud: response.headers.get('x-scrypted-cloud') === 'true',
directAddress: response.headers.get('x-scrypted-direct-address'),
cloudAddress: response.headers.get('x-scrypted-cloud-address'),
};
}
@@ -786,7 +811,7 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
return value;
}
const { port, proxyId } = clusterObject;
const { port, proxyId } = clusterObject;
// check if object is already connected
const resolved = await resolveObject(proxyId, port);

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/amcrest",
"version": "0.0.131",
"version": "0.0.132",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@scrypted/amcrest",
"version": "0.0.131",
"version": "0.0.132",
"license": "Apache",
"dependencies": {
"@scrypted/common": "file:../../common",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/amcrest",
"version": "0.0.131",
"version": "0.0.132",
"description": "Amcrest Plugin for Scrypted",
"author": "Scrypted",
"license": "Apache",

View File

@@ -1,6 +1,5 @@
import { AuthFetchCredentialState, AuthFetchOptions, authHttpFetch } from '@scrypted/common/src/http-auth-fetch';
import { AuthFetchCredentialState, HttpFetchOptions, authHttpFetch } from '@scrypted/common/src/http-auth-fetch';
import { Readable } from 'stream';
import { HttpFetchOptions, HttpFetchResponseType } from '../../../server/src/http-fetch-helpers';
import { getDeviceInfo } from './probe';
export enum AmcrestEvent {
@@ -31,17 +30,15 @@ export class AmcrestCameraClient {
};
}
async request<T extends HttpFetchResponseType>(urlOrOptions: string | URL | HttpFetchOptions<T>, body?: Readable) {
const options: AuthFetchOptions<T> = {
async request(urlOrOptions: string | URL | HttpFetchOptions<Readable>, body?: Readable) {
const response = await authHttpFetch({
...typeof urlOrOptions !== 'string' && !(urlOrOptions instanceof URL) ? urlOrOptions : {
url: urlOrOptions,
},
rejectUnauthorized: false,
credential: this.credential,
body,
};
const response = await authHttpFetch(options);
});
return response;
}

View File

@@ -495,6 +495,8 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
return this.onvifIntercom.startIntercom(media);
}
const doorbellType = this.storage.getItem('doorbellType');
// not sure if this all works, since i don't actually have a doorbell.
// good luck!
const channel = this.getRtspChannel() || '1';
@@ -505,12 +507,29 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
const args = ffmpegInput.inputArguments.slice();
args.unshift('-hide_banner');
args.push(
"-vn",
'-acodec', 'aac',
'-f', 'adts',
'pipe:3',
);
let contentType: string;
if (doorbellType == DAHUA_DOORBELL_TYPE) {
args.push(
"-vn",
'-acodec', 'pcm_alaw',
'-ac', '1',
'-ar', '8000',
'-sample_fmt', 's16',
'-f', 'alaw',
'pipe:3',
);
contentType = 'Audio/G.711A';
}
else {
args.push(
"-vn",
'-acodec', 'aac',
'-f', 'adts',
'pipe:3',
);
contentType = 'Audio/AAC';
}
this.console.log('ffmpeg intercom', args);
@@ -533,7 +552,7 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
url,
method: 'POST',
headers: {
'Content-Type': 'Audio/AAC',
'Content-Type': contentType,
'Content-Length': '9999999'
},
responseType: 'readable',
@@ -588,7 +607,7 @@ class AmcrestProvider extends RtspProvider {
const username = settings.username?.toString();
const password = settings.password?.toString();
const skipValidate = settings.skipValidate === 'true';
const skipValidate = settings.skipValidate?.toString() === 'true';
let twoWayAudio: string;
if (!skipValidate) {
const api = new AmcrestCameraClient(httpAddress, username, password, this.console);

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/core",
"version": "0.2.3",
"version": "0.3.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/core",
"version": "0.2.3",
"version": "0.3.3",
"license": "Apache-2.0",
"dependencies": {
"@scrypted/common": "file:../../common",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/core",
"version": "0.2.3",
"version": "0.3.3",
"description": "Scrypted Core plugin. Provides the UI, websocket, and engine.io APIs.",
"author": "Scrypted",
"license": "Apache-2.0",

View File

@@ -47,37 +47,45 @@ export class Scheduler {
const day = future.getDay();
if (!days[day])
continue;
source.log.i(`event will fire at ${future.toLocaleString()}`);
return future;
}
source.log.w('event will never fire');
}
const when = reschedule();
if (!when) {
return {
removeListener() {
}
}
}
const delay = when.getTime() - Date.now();
source.log.i(`event will fire in ${Math.round(delay / 60 / 1000)} minutes.`);
let timeout = setTimeout(() => {
reschedule();
let timeout: NodeJS.Timeout = null;
let when: Date = null;
function timerCb() {
timeout = null;
const prevWhen = when;
setupTimer();
callback(ret, {
eventId: undefined,
eventInterface: 'Scheduler',
eventTime: Date.now(),
}, when)
}, delay);
}, prevWhen)
}
function setupTimer() {
when = reschedule();
if (when) {
const delay = when.getTime() - Date.now();
source.log.i(`event will fire in ${Math.round(delay / 60 / 1000)} minutes.`);
timeout = setTimeout(timerCb, delay);
}
}
setupTimer();
return {
removeListener() {
clearTimeout(timeout);
if (timeout) {
clearTimeout(timeout);
}
timeout = null;
when = null;
}
}
}

View File

@@ -1,4 +1,4 @@
import { tsCompile } from '@scrypted/common/src/eval/scrypted-eval';
import { readFileAsString, tsCompile } from '@scrypted/common/src/eval/scrypted-eval';
import sdk, { DeviceProvider, EngineIOHandler, HttpRequest, HttpRequestHandler, HttpResponse, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, Setting, Settings, SettingValue } from '@scrypted/sdk';
import { StorageSettings } from "@scrypted/sdk/storage-settings";
import fs from 'fs';
@@ -64,8 +64,7 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Eng
constructor() {
super();
this.indexHtml = fs.readFileSync('dist/index.html').toString();
this.indexHtml = readFileAsString('dist/index.html');
(async () => {
await deviceManager.onDeviceDiscovered(

View File

@@ -3,7 +3,6 @@ import { scryptedEval } from "./scrypted-eval";
import { monacoEvalDefaults } from "./monaco";
import { createScriptDevice, ScriptDeviceImpl } from "@scrypted/common/src/eval/scrypted-eval";
import { ScriptCoreNativeId } from "./script-core";
import { PluginAPIProxy } from "../../../server/src/plugin/plugin-api";
const { deviceManager } = sdk;

View File

@@ -111,22 +111,22 @@
"dependencies": {
"@scrypted/sdk": "file:../sdk",
"@scrypted/server": "file:../server",
"http-auth-utils": "^3.0.2",
"node-fetch-commonjs": "^3.1.1",
"typescript": "^4.4.3"
"http-auth-utils": "^5.0.1",
"typescript": "^5.3.3"
},
"devDependencies": {
"@types/node": "^16.9.0"
"@types/node": "^20.11.0",
"ts-node": "^10.9.2"
}
},
"../../../sdk": {
"name": "@scrypted/sdk",
"version": "0.2.108",
"version": "0.3.5",
"license": "ISC",
"dependencies": {
"@babel/preset-typescript": "^7.18.6",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"axios": "^1.6.5",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.15.9",
@@ -158,7 +158,7 @@
},
"../../../sdk/types": {
"name": "@scrypted/types",
"version": "0.2.99",
"version": "0.3.5",
"license": "ISC",
"devDependencies": {
"@types/rimraf": "^3.0.2",

View File

@@ -137,7 +137,7 @@ export default {
// cert may need to be reaccepted? Server is down? Go to the
// server root to force the network error to bypass the PWA cache.
if (
e.toString().includes("Network Error") &&
(e.toString().includes("Network Error") || e.toString().includes("Load failed")) &&
window.location.href.startsWith("https:")
) {
window.location = "/";

View File

@@ -21,27 +21,18 @@
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
v-if="!canUpdate"
small
text
href="https://github.com/koush/scrypted#installation"
>More Information</v-btn
>
<v-btn v-if="!canUpdate" small text href="https://github.com/koush/scrypted#installation">More
Information</v-btn>
<v-dialog v-else v-model="updateAndRestart" width="500">
<template v-slot:activator="{ on }">
<v-btn small text color="red" v-on="on"
>Update and Restart Scrypted</v-btn
>
<v-btn small text color="red" v-on="on">Update and Restart Scrypted</v-btn>
</template>
<v-card color="red" dark>
<v-card-title primary-title>Restart Scrypted</v-card-title>
<v-card-text
>Are you sure you want to restart the Scrypted
service?</v-card-text
>
<v-card-text>Are you sure you want to restart the Scrypted
service?</v-card-text>
<v-card-text>{{ restartStatus }}</v-card-text>
<v-divider></v-divider>
@@ -67,10 +58,11 @@
</v-card>
<v-card class="mt-2" v-if="showRestart">
<v-toolbar
><v-toolbar-title>Server Management</v-toolbar-title></v-toolbar
>
<v-toolbar><v-toolbar-title>Server Management</v-toolbar-title></v-toolbar>
<v-card-actions>
<v-btn text :href="backupUrl" color="info">Backup</v-btn>
<v-btn text color="info" @click="restoreClick">Restore</v-btn>
<input type="file" ref="restoreFile" style="display: none;" @change="restore" />
<v-spacer></v-spacer>
<v-dialog v-model="restart" width="500">
<template v-slot:activator="{ on }">
@@ -80,10 +72,8 @@
<v-card color="red" dark>
<v-card-title primary-title>Restart Scrypted</v-card-title>
<v-card-text
>Are you sure you want to restart the Scrypted
service?</v-card-text
>
<v-card-text>Are you sure you want to restart the Scrypted
service?</v-card-text>
<v-card-text>{{ restartStatus }}</v-card-text>
<v-divider></v-divider>
@@ -103,7 +93,8 @@
<script>
import { checkServerUpdate } from "../plugin/plugin";
import Settings from "../../interfaces/Settings.vue"
import {createSystemSettingsDevice} from './system-settings';
import { createSystemSettingsDevice } from './system-settings';
import { combineBaseUrl, getCurrentBaseUrl } from "../../../../../../packages/client/src";
export default {
components: {
@@ -121,11 +112,35 @@ export default {
showRestart: false,
};
},
computed: {
backupUrl() {
const baseUrl = getCurrentBaseUrl();
return combineBaseUrl(baseUrl, 'web/component/backup');
},
},
mounted() {
this.loadEnv();
this.checkUpdateAvailable();
},
methods: {
async restoreClick() {
const restoreFile = this.$refs.restoreFile;
restoreFile.click();
},
async restore() {
const restoreFile = this.$refs.restoreFile;
const file = restoreFile.files[0];
if (!file)
return;
console.log(file);
const fileBlob = new Blob([file]);
const baseUrl = getCurrentBaseUrl();
const restoreUrl = combineBaseUrl(baseUrl, 'web/component/restore');
await fetch(restoreUrl, {
method: 'POST',
body: fileBlob,
});
},
async checkUpdateAvailable() {
const info = await this.$scrypted.systemManager.getComponent("info");
const version = await info.getVersion();

View File

@@ -33,17 +33,56 @@ export default {
const termSvcRaw = this.$scrypted.systemManager.getDeviceByName("@scrypted/core");
const termSvc = await termSvcRaw.getDevice("terminalservice");
const termSvcDirect = await this.$scrypted.connectRPCObject(termSvc);
const queue = createAsyncQueue();
const dataQueue = createAsyncQueue();
const ctrlQueue = createAsyncQueue();
queue.enqueue(JSON.stringify({ interactive: true }));
queue.enqueue(JSON.stringify({ dim: { cols: term.cols, rows: term.rows } }));
ctrlQueue.enqueue({ interactive: true });
ctrlQueue.enqueue({ dim: { cols: term.cols, rows: term.rows } });
term.onData(data => queue.enqueue(Buffer.from(data, 'utf8')));
term.onBinary(data => queue.enqueue(Buffer.from(data, 'binary')));
term.onResize(dim => queue.enqueue(JSON.stringify({ dim })));
let bufferedLength = 0;
const MAX_BUFFERED_LENGTH = 64000;
async function dataQueueEnqueue(data) {
bufferedLength += data.length;
const promise = dataQueue.enqueue(data).then(() => bufferedLength -= data.length);
if (bufferedLength >= MAX_BUFFERED_LENGTH) {
term.setOption("disableStdin", true);
await promise;
if (bufferedLength < MAX_BUFFERED_LENGTH)
term.setOption("disableStdin", false);
}
}
const localGenerator = queue.queue;
const remoteGenerator = await termSvcDirect.connectStream(localGenerator);
term.onData(data => dataQueueEnqueue(Buffer.from(data, 'utf8')));
term.onBinary(data => dataQueueEnqueue(Buffer.from(data, 'binary')));
term.onResize(dim => {
ctrlQueue.enqueue({ dim });
ctrlQueue.enqueue(Buffer.alloc(0));
});
async function* localGenerator() {
while (true) {
const ctrlBuffers = ctrlQueue.clear();
if (ctrlBuffers.length) {
for (const ctrl of ctrlBuffers) {
yield JSON.stringify(ctrl);
}
continue;
}
const dataBuffers = dataQueue.clear();
if (dataBuffers.length === 0) {
const buf = await dataQueue.dequeue();
if (buf.length)
yield buf;
continue;
}
const concat = Buffer.concat(dataBuffers);
if (concat.length)
yield concat;
}
}
const remoteGenerator = await termSvcDirect.connectStream(localGenerator());
for await (const message of remoteGenerator) {
if (!message) {

View File

@@ -42,15 +42,28 @@ export function createSystemSettingsDevice(systemManager: SystemManager): Scrypt
const results = systemSettings.map(async d => {
const settings = await d.getSettings();
for (const setting of settings) {
const subgroup = setting.group;
if (d.pluginId === '@scrypted/core')
setting.group = 'General';
else
setting.group = d.name;
setting.subgroup = subgroup;
setting.key = d.id + ':' + setting.key;
}
return settings;
});
return (await Promise.all(results)).flat();
const ret = (await Promise.all(results)).flat();
ret.sort((a, b) => {
if (a.group === 'General') {
if (b.group === 'General')
return 0;
return -1;
}
if (b.group === 'General')
return 1;
return 0;
});
return ret;
},
async putSetting(key, value) {
const [id, realKey] = key.split(':');

View File

@@ -8,7 +8,6 @@
"name": "@scrypted/doorbird",
"version": "0.0.2",
"dependencies": {
"@koush/axios-digest-auth": "^0.8.5",
"doorbird": "^2.1.2"
},
"devDependencies": {
@@ -26,23 +25,24 @@
"dependencies": {
"@scrypted/sdk": "file:../sdk",
"@scrypted/server": "file:../server",
"http-auth-utils": "^3.0.2",
"http-auth-utils": "^5.0.1",
"node-fetch-commonjs": "^3.1.1",
"typescript": "^4.4.3"
"typescript": "^5.3.3"
},
"devDependencies": {
"@types/node": "^16.9.0"
"@types/node": "^20.10.8",
"ts-node": "^10.9.2"
}
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.2.103",
"version": "0.3.4",
"dev": true,
"license": "ISC",
"dependencies": {
"@babel/preset-typescript": "^7.18.6",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"axios": "^1.6.5",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.15.9",
@@ -72,23 +72,6 @@
"typedoc": "^0.23.21"
}
},
"node_modules/@koush/axios-digest-auth": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/@koush/axios-digest-auth/-/axios-digest-auth-0.8.5.tgz",
"integrity": "sha512-EZMM0gMJ3hMUD4EuUqSwP6UGt5Vmw2TZtY7Ypec55AnxkExSXM0ySgPtqkAcnL43g1R27yAg/dQL7dRTLMqO3Q==",
"dependencies": {
"auth-header": "^1.0.0",
"axios": "^0.21.4"
}
},
"node_modules/@koush/axios-digest-auth/node_modules/axios": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"dependencies": {
"follow-redirects": "^1.14.0"
}
},
"node_modules/@scrypted/common": {
"resolved": "../../common",
"link": true
@@ -108,17 +91,12 @@
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/auth-header": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/auth-header/-/auth-header-1.0.0.tgz",
"integrity": "sha512-CPPazq09YVDUNNVWo4oSPTQmtwIzHusZhQmahCKvIsk0/xH6U3QsMAv3sM+7+Q0B1K2KJ/Q38OND317uXs4NHA=="
},
"node_modules/axios": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.5.tgz",
"integrity": "sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw==",
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz",
"integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==",
"dependencies": {
"follow-redirects": "^1.15.0",
"follow-redirects": "^1.15.4",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
@@ -199,9 +177,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"funding": [
{
"type": "individual",

View File

@@ -33,7 +33,6 @@
]
},
"dependencies": {
"@koush/axios-digest-auth": "^0.8.5",
"doorbird": "^2.1.2"
},
"devDependencies": {

View File

@@ -5,8 +5,8 @@ import { ffmpegLogInitialOutput, safePrintFFmpegArguments } from "@scrypted/comm
import net from 'net';
import { randomBytes } from 'crypto';
import { PassThrough, Readable } from "stream";
import AxiosDigestAuth from '@koush/axios-digest-auth';
import { readLength } from "@scrypted/common/src/read-stream";
import { authHttpFetch } from "@scrypted/common/src/http-auth-fetch";
import { ApiRingEvent, ApiMotionEvent, DoorbirdAPI } from "./doorbird-api";
const { deviceManager, mediaManager } = sdk;
@@ -27,7 +27,7 @@ class DoorbirdCamera extends ScryptedDeviceBase implements Intercom, Camera, Vid
this.binaryState = false;
this.doorbellAudioActive = false;
this.updateDeviceInfo();
this.updateDeviceInfo();
}
getDoorbirdApi() {
@@ -49,8 +49,8 @@ class DoorbirdCamera extends ScryptedDeviceBase implements Intercom, Camera, Vid
this.console?.log("Time:", event.timestamp);
this.triggerMotionSensor();
});
this.getDoorbirdApi()?.startEventSocket();
}
this.getDoorbirdApi()?.startEventSocket();
}
return this.doorbirdApi;
}
@@ -157,7 +157,7 @@ class DoorbirdCamera extends ScryptedDeviceBase implements Intercom, Camera, Vid
this.stopAudioTransmitter();
this.stopAudioReceiver();
}
async startAudioTransmitter(media: MediaObject): Promise<void> {
const ffmpegInput: FFmpegInput = JSON.parse((await mediaManager.convertMediaObjectToBuffer(media, ScryptedMimeTypes.FFmpegInput)).toString());
@@ -194,13 +194,13 @@ class DoorbirdCamera extends ScryptedDeviceBase implements Intercom, Camera, Vid
this.console.log('Doorbird: audio transmitter started.');
const passthrough = new PassThrough();
const digestAuth = new AxiosDigestAuth({
username,
password
});
digestAuth.request({
authHttpFetch({
method: 'POST',
url: audioTxUrl,
credential: {
username,
password,
},
headers: {
'Content-Type': 'audio/basic',
'Content-Length': '9999999'
@@ -293,7 +293,8 @@ class DoorbirdCamera extends ScryptedDeviceBase implements Intercom, Camera, Vid
// this is a hint to let homekit, et al, know that it's OPUS audio and does not need transcoding.
codec: 'pcm_mulaw',
}
}]; }
}];
}
async getVideoStream(options?: ResponseMediaStreamOptions): Promise<MediaObject> {
@@ -327,7 +328,7 @@ class DoorbirdCamera extends ScryptedDeviceBase implements Intercom, Camera, Vid
if (this.audioSilenceProcess)
return;
this.console.log('Doorbird: starting audio silence generator...')
this.console.log('Doorbird: starting audio silence generator...')
const ffmpegPath = await mediaManager.getFFmpegPath();
const ffmpegArgs = [
@@ -552,10 +553,10 @@ export class DoorbirdCamProvider extends ScryptedDeviceBase implements DevicePro
}
async releaseDevice(id: string, nativeId: string): Promise<void> {
if( this.devices.delete( nativeId ) ) {
this.console.log("Doorbird: Removed device from list: " + id + " / " + nativeId )
if (this.devices.delete(nativeId)) {
this.console.log("Doorbird: Removed device from list: " + id + " / " + nativeId)
}
}
}
createCamera(nativeId: string): DoorbirdCamera {
return new DoorbirdCamera(nativeId, this);

View File

@@ -1,18 +1,17 @@
{
"name": "@scrypted/hikvision",
"version": "0.0.133",
"version": "0.0.134",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/hikvision",
"version": "0.0.133",
"version": "0.0.134",
"license": "Apache",
"dependencies": {
"@scrypted/common": "file:../../common",
"@scrypted/sdk": "file:../../sdk",
"@types/xml2js": "^0.4.11",
"http-auth-utils": "^3.0.2",
"lodash": "^4.17.21",
"xml2js": "^0.6.0"
},
@@ -27,7 +26,7 @@
"dependencies": {
"@scrypted/sdk": "file:../sdk",
"@scrypted/server": "file:../server",
"http-auth-utils": "^3.0.2",
"http-auth-utils": "^5.0.1",
"node-fetch-commonjs": "^3.1.1",
"typescript": "^5.3.3"
},
@@ -43,7 +42,7 @@
"dependencies": {
"@babel/preset-typescript": "^7.18.6",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"axios": "^1.6.5",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.15.9",
@@ -97,17 +96,6 @@
"@types/node": "*"
}
},
"node_modules/http-auth-utils": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/http-auth-utils/-/http-auth-utils-3.0.2.tgz",
"integrity": "sha512-cQ8957aiUX0lgV1620uIGKGJc0sEuD/QK4ueZ0hb60MGbO0f6ahcuIgPjamAD98D/AUGizKVm+dNvUVHs0f4Ow==",
"dependencies": {
"yerror": "^6.0.0"
},
"engines": {
"node": ">=12.19.0"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@@ -137,14 +125,6 @@
"engines": {
"node": ">=4.0"
}
},
"node_modules/yerror": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/yerror/-/yerror-6.2.1.tgz",
"integrity": "sha512-WPZgybhCBzsMSSqGYBnj20NZo4FiFKQG0+i/21cYGVd4B7eYtvYDOpjk/0e8UM1eVHJ+4Ja6bZ7TAjHH6mk+ew==",
"engines": {
"node": ">=12.19.0"
}
}
},
"dependencies": {
@@ -154,7 +134,7 @@
"@scrypted/sdk": "file:../sdk",
"@scrypted/server": "file:../server",
"@types/node": "^20.10.8",
"http-auth-utils": "^3.0.2",
"http-auth-utils": "^5.0.1",
"node-fetch-commonjs": "^3.1.1",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
@@ -167,7 +147,7 @@
"@types/node": "^18.11.18",
"@types/stringify-object": "^4.0.0",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"axios": "^1.6.5",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.15.9",
@@ -197,14 +177,6 @@
"@types/node": "*"
}
},
"http-auth-utils": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/http-auth-utils/-/http-auth-utils-3.0.2.tgz",
"integrity": "sha512-cQ8957aiUX0lgV1620uIGKGJc0sEuD/QK4ueZ0hb60MGbO0f6ahcuIgPjamAD98D/AUGizKVm+dNvUVHs0f4Ow==",
"requires": {
"yerror": "^6.0.0"
}
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@@ -228,11 +200,6 @@
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
},
"yerror": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/yerror/-/yerror-6.2.1.tgz",
"integrity": "sha512-WPZgybhCBzsMSSqGYBnj20NZo4FiFKQG0+i/21cYGVd4B7eYtvYDOpjk/0e8UM1eVHJ+4Ja6bZ7TAjHH6mk+ew=="
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/hikvision",
"version": "0.0.133",
"version": "0.0.134",
"description": "Hikvision Plugin for Scrypted",
"author": "Scrypted",
"license": "Apache",
@@ -38,7 +38,6 @@
"@scrypted/common": "file:../../common",
"@scrypted/sdk": "file:../../sdk",
"@types/xml2js": "^0.4.11",
"http-auth-utils": "^3.0.2",
"lodash": "^4.17.21",
"xml2js": "^0.6.0"
},

View File

@@ -1,7 +1,6 @@
import { AuthFetchCredentialState, AuthFetchOptions, authHttpFetch } from '@scrypted/common/src/http-auth-fetch';
import { IncomingMessage, RequestOptions } from 'http';
import { AuthFetchCredentialState, HttpFetchOptions, HttpFetchResponseType, authHttpFetch } from '@scrypted/common/src/http-auth-fetch';
import { IncomingMessage } from 'http';
import { Readable } from 'stream';
import { HttpFetchOptions, HttpFetchResponseType } from '../../../server/src/http-fetch-helpers';
import { getDeviceInfo } from './probe';
export function getChannel(channel: string) {
@@ -41,17 +40,15 @@ export class HikvisionCameraAPI {
};
}
async request<T extends HttpFetchResponseType>(urlOrOptions: string | URL | HttpFetchOptions<T>, body?: Readable) {
const options: AuthFetchOptions<T> = {
async request(urlOrOptions: string | URL | HttpFetchOptions<Readable>, body?: Readable) {
const response = await authHttpFetch({
...typeof urlOrOptions !== 'string' && !(urlOrOptions instanceof URL) ? urlOrOptions : {
url: urlOrOptions,
},
rejectUnauthorized: false,
credential: this.credential,
body,
};
const response = await authHttpFetch(options);
});
return response;
}

View File

@@ -525,7 +525,7 @@ class HikvisionProvider extends RtspProvider {
const username = settings.username?.toString();
const password = settings.password?.toString();
const skipValidate = settings.skipValidate === 'true';
const skipValidate = settings.skipValidate?.toString() === 'true';
let twoWayAudio: string;
if (!skipValidate) {
const api = new HikvisionCameraAPI(httpAddress, username, password, this.console);

View File

@@ -1,6 +1,5 @@
import { AuthFetchCredentialState, authHttpFetch } from '@scrypted/common/src/http-auth-fetch';
import { checkStatus } from '../../../server/src/http-fetch-helpers';
import { checkStatus } from '../../../server/src/fetch';
export async function getDeviceInfo(credential: AuthFetchCredentialState, address: string) {
const response = await authHttpFetch({

View File

@@ -1,26 +1,26 @@
{
"name": "@scrypted/homekit",
"version": "1.2.33",
"version": "1.2.36",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/homekit",
"version": "1.2.33",
"version": "1.2.36",
"dependencies": {
"@koush/werift-src": "file:../../external/werift",
"check-disk-space": "^3.3.1",
"hap-nodejs": "^0.11.0",
"check-disk-space": "^3.4.0",
"hap-nodejs": "^0.11.1",
"lodash": "^4.17.21",
"mkdirp": "^2.1.6"
"mkdirp": "^3.0.1"
},
"devDependencies": {
"@scrypted/common": "file:../../common",
"@scrypted/sdk": "file:../../sdk",
"@types/debug": "^4.1.7",
"@types/lodash": "^4.14.192",
"@types/node": "^18.15.11",
"@types/url-parse": "^1.4.8"
"@types/debug": "^4.1.12",
"@types/lodash": "^4.14.202",
"@types/node": "^20.11.0",
"@types/url-parse": "^1.4.11"
}
},
"../../common": {
@@ -31,12 +31,12 @@
"dependencies": {
"@scrypted/sdk": "file:../sdk",
"@scrypted/server": "file:../server",
"http-auth-utils": "^3.0.2",
"node-fetch-commonjs": "^3.1.1",
"typescript": "^4.4.3"
"http-auth-utils": "^5.0.1",
"typescript": "^5.3.3"
},
"devDependencies": {
"@types/node": "^16.9.0"
"@types/node": "^20.11.0",
"ts-node": "^10.9.2"
}
},
"../../external/werift": {
@@ -47,25 +47,26 @@
"examples/*"
],
"devDependencies": {
"@types/jest": "^29.2.4",
"@types/node": "^18.11.18",
"@typescript-eslint/eslint-plugin": "^5.47.1",
"@typescript-eslint/parser": "^5.47.1",
"eslint": "^8.30.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-simple-import-sort": "^8.0.0",
"jest": "^29.3.1",
"@types/jest": "^29.5.11",
"@types/node": "^20.10.4",
"@typescript-eslint/eslint-plugin": "^6.14.0",
"@typescript-eslint/parser": "^6.14.0",
"eslint": "^8.55.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-simple-import-sort": "^10.0.0",
"jest": "^29.7.0",
"knip": "^3.7.0",
"node-actionlint": "^1.2.2",
"organize-imports-cli": "^0.10.0",
"prettier": "^2.8.1",
"prettier": "^3.1.1",
"process": "^0.11.10",
"ts-jest": "^29.0.3",
"ts-node": "^10.9.1",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.2",
"ts-node-dev": "^2.0.0",
"typedoc": "^0.23.23",
"typedoc-plugin-markdown": "^3.14.0",
"typescript": "^4.9.4"
"typedoc": "0.25.4",
"typedoc-plugin-markdown": "3.17.1",
"typescript": "5.0.4"
},
"engines": {
"node": ">=16"
@@ -126,13 +127,13 @@
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.2.86",
"version": "0.3.4",
"dev": true,
"license": "ISC",
"dependencies": {
"@babel/preset-typescript": "^7.18.6",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"axios": "^1.6.5",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.15.9",
@@ -220,9 +221,9 @@
}
},
"node_modules/@homebridge/dbus-native": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@homebridge/dbus-native/-/dbus-native-0.5.0.tgz",
"integrity": "sha512-ei0jyHE/uNDl/6D6heRwsqnESrrXuSlfp+xlwGfg3mo1OqhKvyb/Kp73uxQyOJ3f1T1ocLSyA5uzoR1AbfaXIQ==",
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@homebridge/dbus-native/-/dbus-native-0.5.1.tgz",
"integrity": "sha512-7xXz3R1W/kcbfQOGp32y4K7etqtowICR1vpx8j85KwPYXbNQrgiZ3zcwDYgDGBWq3FD9xzsW7h4YWJ4vTR2seQ==",
"dependencies": {
"@homebridge/long": "^5.2.1",
"@homebridge/put": "~0.0.8",
@@ -230,7 +231,7 @@
"hexy": "^0.2.10",
"minimist": "^1.2.6",
"safe-buffer": "^5.1.1",
"xml2js": "^0.4.17"
"xml2js": "^0.5.0"
},
"bin": {
"dbus2js": "bin/dbus2js.js"
@@ -267,18 +268,18 @@
"link": true
},
"node_modules/@types/debug": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz",
"integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==",
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
"dev": true,
"dependencies": {
"@types/ms": "*"
}
},
"node_modules/@types/lodash": {
"version": "4.14.192",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.192.tgz",
"integrity": "sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==",
"version": "4.14.202",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz",
"integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
"dev": true
},
"node_modules/@types/ms": {
@@ -288,15 +289,18 @@
"dev": true
},
"node_modules/@types/node": {
"version": "18.15.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
"dev": true
"version": "20.11.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.0.tgz",
"integrity": "sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/url-parse": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/@types/url-parse/-/url-parse-1.4.8.tgz",
"integrity": "sha512-zqqcGKyNWgTLFBxmaexGUKQyWqeG7HjXj20EuQJSJWwXe54BjX0ihIo5cJB9yAQzH8dNugJ9GvkBYMjPXs/PJw==",
"version": "1.4.11",
"resolved": "https://registry.npmjs.org/@types/url-parse/-/url-parse-1.4.11.tgz",
"integrity": "sha512-FKvKIqRaykZtd4n47LbK/W/5fhQQ1X7cxxzG9A48h0BGN+S04NH7ervcCjM8tyR0lyGru83FAHSmw2ObgKoESg==",
"dev": true
},
"node_modules/array-flatten": {
@@ -345,11 +349,11 @@
}
},
"node_modules/check-disk-space": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/check-disk-space/-/check-disk-space-3.3.1.tgz",
"integrity": "sha512-iOrT8yCZjSnyNZ43476FE2rnssvgw5hnuwOM0hm8Nj1qa0v4ieUUEbCyxxsEliaoDUb/75yCOL71zkDiDBLbMQ==",
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/check-disk-space/-/check-disk-space-3.4.0.tgz",
"integrity": "sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw==",
"engines": {
"node": ">=12"
"node": ">=16"
}
},
"node_modules/debug": {
@@ -486,9 +490,12 @@
"integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g=="
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/functions-have-names": {
"version": "1.2.3",
@@ -531,12 +538,12 @@
}
},
"node_modules/hap-nodejs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/hap-nodejs/-/hap-nodejs-0.11.0.tgz",
"integrity": "sha512-ZKSc/DIECXH1vSlruv6tBVcO+LF/BDtjdVk7IIiAAS+KKjw9PylkXbtdU23mmLhM69BsWl9u+BuToAfkf0voSw==",
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/hap-nodejs/-/hap-nodejs-0.11.1.tgz",
"integrity": "sha512-hJuGyjng2jlzhZsviWCldaokT7l7BE3iGmWdlE6DNmQFDTmiBN3deNksAZ2nt7qp5jYEv7ZUvW7WBZqJsLh3ww==",
"dependencies": {
"@homebridge/ciao": "^1.1.5",
"@homebridge/dbus-native": "^0.5.0",
"@homebridge/dbus-native": "^0.5.1",
"bonjour-hap": "~3.6.4",
"debug": "^4.3.4",
"fast-srp-hap": "~2.0.4",
@@ -856,9 +863,9 @@
}
},
"node_modules/mkdirp": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz",
"integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==",
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
"integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
"bin": {
"mkdirp": "dist/cjs/src/bin.js"
},
@@ -1012,9 +1019,9 @@
]
},
"node_modules/sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz",
"integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA=="
},
"node_modules/side-channel": {
"version": "1.0.4",
@@ -1097,6 +1104,12 @@
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/which-boxed-primitive": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
@@ -1146,9 +1159,9 @@
}
},
"node_modules/xml2js": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
"integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
"integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
"dependencies": {
"sax": ">=0.6.0",
"xmlbuilder": "~11.0.0"
@@ -1157,7 +1170,7 @@
"node": ">=4.0.0"
}
},
"node_modules/xmlbuilder": {
"node_modules/xml2js/node_modules/xmlbuilder": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
@@ -1179,9 +1192,9 @@
}
},
"@homebridge/dbus-native": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@homebridge/dbus-native/-/dbus-native-0.5.0.tgz",
"integrity": "sha512-ei0jyHE/uNDl/6D6heRwsqnESrrXuSlfp+xlwGfg3mo1OqhKvyb/Kp73uxQyOJ3f1T1ocLSyA5uzoR1AbfaXIQ==",
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@homebridge/dbus-native/-/dbus-native-0.5.1.tgz",
"integrity": "sha512-7xXz3R1W/kcbfQOGp32y4K7etqtowICR1vpx8j85KwPYXbNQrgiZ3zcwDYgDGBWq3FD9xzsW7h4YWJ4vTR2seQ==",
"requires": {
"@homebridge/long": "^5.2.1",
"@homebridge/put": "~0.0.8",
@@ -1189,7 +1202,7 @@
"hexy": "^0.2.10",
"minimist": "^1.2.6",
"safe-buffer": "^5.1.1",
"xml2js": "^0.4.17"
"xml2js": "^0.5.0"
}
},
"@homebridge/long": {
@@ -1205,25 +1218,26 @@
"@koush/werift-src": {
"version": "file:../../external/werift",
"requires": {
"@types/jest": "^29.2.4",
"@types/node": "^18.11.18",
"@typescript-eslint/eslint-plugin": "^5.47.1",
"@typescript-eslint/parser": "^5.47.1",
"eslint": "^8.30.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-simple-import-sort": "^8.0.0",
"jest": "^29.3.1",
"@types/jest": "^29.5.11",
"@types/node": "^20.10.4",
"@typescript-eslint/eslint-plugin": "^6.14.0",
"@typescript-eslint/parser": "^6.14.0",
"eslint": "^8.55.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-simple-import-sort": "^10.0.0",
"jest": "^29.7.0",
"knip": "^3.7.0",
"node-actionlint": "^1.2.2",
"organize-imports-cli": "^0.10.0",
"prettier": "^2.8.1",
"prettier": "^3.1.1",
"process": "^0.11.10",
"ts-jest": "^29.0.3",
"ts-node": "^10.9.1",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.2",
"ts-node-dev": "^2.0.0",
"typedoc": "^0.23.23",
"typedoc-plugin-markdown": "^3.14.0",
"typescript": "^4.9.4"
"typedoc": "0.25.4",
"typedoc-plugin-markdown": "3.17.1",
"typescript": "5.0.4"
}
},
"@leichtgewicht/ip-codec": {
@@ -1236,10 +1250,10 @@
"requires": {
"@scrypted/sdk": "file:../sdk",
"@scrypted/server": "file:../server",
"@types/node": "^16.9.0",
"http-auth-utils": "^3.0.2",
"node-fetch-commonjs": "^3.1.1",
"typescript": "^4.4.3"
"@types/node": "^20.11.0",
"http-auth-utils": "^5.0.1",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
}
},
"@scrypted/sdk": {
@@ -1249,7 +1263,7 @@
"@types/node": "^18.11.18",
"@types/stringify-object": "^4.0.0",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"axios": "^1.6.5",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.15.9",
@@ -1267,18 +1281,18 @@
}
},
"@types/debug": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz",
"integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==",
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
"dev": true,
"requires": {
"@types/ms": "*"
}
},
"@types/lodash": {
"version": "4.14.192",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.192.tgz",
"integrity": "sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==",
"version": "4.14.202",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz",
"integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
"dev": true
},
"@types/ms": {
@@ -1288,15 +1302,18 @@
"dev": true
},
"@types/node": {
"version": "18.15.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
"dev": true
"version": "20.11.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.0.tgz",
"integrity": "sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==",
"dev": true,
"requires": {
"undici-types": "~5.26.4"
}
},
"@types/url-parse": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/@types/url-parse/-/url-parse-1.4.8.tgz",
"integrity": "sha512-zqqcGKyNWgTLFBxmaexGUKQyWqeG7HjXj20EuQJSJWwXe54BjX0ihIo5cJB9yAQzH8dNugJ9GvkBYMjPXs/PJw==",
"version": "1.4.11",
"resolved": "https://registry.npmjs.org/@types/url-parse/-/url-parse-1.4.11.tgz",
"integrity": "sha512-FKvKIqRaykZtd4n47LbK/W/5fhQQ1X7cxxzG9A48h0BGN+S04NH7ervcCjM8tyR0lyGru83FAHSmw2ObgKoESg==",
"dev": true
},
"array-flatten": {
@@ -1336,9 +1353,9 @@
}
},
"check-disk-space": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/check-disk-space/-/check-disk-space-3.3.1.tgz",
"integrity": "sha512-iOrT8yCZjSnyNZ43476FE2rnssvgw5hnuwOM0hm8Nj1qa0v4ieUUEbCyxxsEliaoDUb/75yCOL71zkDiDBLbMQ=="
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/check-disk-space/-/check-disk-space-3.4.0.tgz",
"integrity": "sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw=="
},
"debug": {
"version": "4.3.4",
@@ -1448,9 +1465,9 @@
"integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g=="
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
},
"functions-have-names": {
"version": "1.2.3",
@@ -1481,12 +1498,12 @@
}
},
"hap-nodejs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/hap-nodejs/-/hap-nodejs-0.11.0.tgz",
"integrity": "sha512-ZKSc/DIECXH1vSlruv6tBVcO+LF/BDtjdVk7IIiAAS+KKjw9PylkXbtdU23mmLhM69BsWl9u+BuToAfkf0voSw==",
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/hap-nodejs/-/hap-nodejs-0.11.1.tgz",
"integrity": "sha512-hJuGyjng2jlzhZsviWCldaokT7l7BE3iGmWdlE6DNmQFDTmiBN3deNksAZ2nt7qp5jYEv7ZUvW7WBZqJsLh3ww==",
"requires": {
"@homebridge/ciao": "^1.1.5",
"@homebridge/dbus-native": "^0.5.0",
"@homebridge/dbus-native": "^0.5.1",
"bonjour-hap": "~3.6.4",
"debug": "^4.3.4",
"fast-srp-hap": "~2.0.4",
@@ -1698,9 +1715,9 @@
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
},
"mkdirp": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz",
"integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A=="
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
"integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="
},
"ms": {
"version": "2.1.2",
@@ -1799,9 +1816,9 @@
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz",
"integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA=="
},
"side-channel": {
"version": "1.0.4",
@@ -1872,6 +1889,12 @@
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="
},
"undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"which-boxed-primitive": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
@@ -1909,18 +1932,20 @@
}
},
"xml2js": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
"integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
"integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
"requires": {
"sax": ">=0.6.0",
"xmlbuilder": "~11.0.0"
},
"dependencies": {
"xmlbuilder": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
}
}
},
"xmlbuilder": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/homekit",
"version": "1.2.33",
"version": "1.2.36",
"description": "HomeKit Plugin for Scrypted",
"scripts": {
"scrypted-setup-project": "scrypted-setup-project",
@@ -35,17 +35,17 @@
},
"dependencies": {
"@koush/werift-src": "file:../../external/werift",
"check-disk-space": "^3.3.1",
"hap-nodejs": "^0.11.0",
"check-disk-space": "^3.4.0",
"hap-nodejs": "^0.11.1",
"lodash": "^4.17.21",
"mkdirp": "^2.1.6"
"mkdirp": "^3.0.1"
},
"devDependencies": {
"@scrypted/common": "file:../../common",
"@scrypted/sdk": "file:../../sdk",
"@types/debug": "^4.1.7",
"@types/lodash": "^4.14.192",
"@types/node": "^18.15.11",
"@types/url-parse": "^1.4.8"
"@types/debug": "^4.1.12",
"@types/lodash": "^4.14.202",
"@types/node": "^20.11.0",
"@types/url-parse": "^1.4.11"
}
}

View File

@@ -1,8 +1,8 @@
import sdk, { FFmpegInput, MediaObject, VideoClip, VideoClipOptions } from '@scrypted/sdk';
import path from 'path';
import fs from 'fs';
import mkdirp from 'mkdirp';
import { mkdirp } from 'mkdirp';
import path from 'path';
const { mediaManager } = sdk;
export const VIDEO_CLIPS_NATIVE_ID = 'save-video-clips';
@@ -98,16 +98,6 @@ export async function getVideoClips(options?: VideoClipOptions, id?: string): Pr
if (options?.endTime)
ret = ret.filter(clip => clip.startTime + clip.duration < options.endTime);
if (options?.reverseOrder)
ret = ret.reverse();
if (options?.startId) {
const startIndex = ret.findIndex(c => c.id === options.startId);
if (startIndex === -1)
throw new Error('startIndex not found');
ret = ret.slice(startIndex);
}
if (options?.count)
ret = ret.slice(0, options.count);

View File

@@ -7,17 +7,17 @@ import { timeoutPromise } from "@scrypted/common/src/promise-utils";
import sdk, { AudioSensor, FFmpegInput, MotionSensor, ScryptedDevice, ScryptedInterface, ScryptedMimeTypes, VideoCamera } from '@scrypted/sdk';
import child_process from "child_process";
import fs from 'fs';
import mkdirp from 'mkdirp';
import { mkdirp } from 'mkdirp';
import net from 'net';
import path from 'path';
import { Duplex, Readable, Writable } from 'stream';
import { } from '../../common';
import { AudioRecordingCodecType, CameraRecordingConfiguration, DataStreamConnection, RecordingPacket } from '../../hap';
import { AudioRecordingCodecType, CameraRecordingConfiguration, RecordingPacket } from '../../hap';
import type { HomeKitPlugin } from "../../main";
import { getCameraRecordingFiles, HksvVideoClip, VIDEO_CLIPS_NATIVE_ID } from './camera-recording-files';
import { checkCompatibleCodec, FORCE_OPUS, transcodingDebugModeWarning } from './camera-utils';
import { NAL_TYPE_DELIMITER, NAL_TYPE_FU_A, NAL_TYPE_IDR, NAL_TYPE_PPS, NAL_TYPE_SEI, NAL_TYPE_SPS, NAL_TYPE_STAP_A } from "./h264-packetizer";
import path from 'path';
import { getDebugMode } from "./camera-debug-mode-storage";
import { HksvVideoClip, VIDEO_CLIPS_NATIVE_ID, getCameraRecordingFiles } from './camera-recording-files';
import { FORCE_OPUS, checkCompatibleCodec, transcodingDebugModeWarning } from './camera-utils';
import { NAL_TYPE_DELIMITER, NAL_TYPE_FU_A, NAL_TYPE_IDR, NAL_TYPE_PPS, NAL_TYPE_SEI, NAL_TYPE_SPS, NAL_TYPE_STAP_A } from "./h264-packetizer";
const { log, mediaManager, deviceManager } = sdk;

View File

@@ -1,7 +1,7 @@
import { sleep } from "@scrypted/common/src/sleep";
import sdk, { AudioSensor, Camera, Intercom, Logger, MotionSensor, ScryptedDevice, ScryptedInterface, VideoCamera } from "@scrypted/sdk";
import throttle from "lodash/throttle";
import { SnapshotRequest, SnapshotRequestCallback } from "../../hap";
import { ResourceRequestReason, SnapshotRequest, SnapshotRequestCallback } from "../../hap";
import type { HomeKitPlugin } from "../../main";
const { systemManager, mediaManager } = sdk;
@@ -14,41 +14,18 @@ function recommendSnapshotPlugin(console: Console, log: Logger, message: string)
}
export function createSnapshotHandler(device: ScryptedDevice & VideoCamera & Camera & MotionSensor & AudioSensor & Intercom, storage: Storage, homekitPlugin: HomeKitPlugin, console: Console) {
let pendingPicture: Promise<Buffer>;
const takePicture = async (request: SnapshotRequest) => {
if (!device.interfaces.includes(ScryptedInterface.Camera))
throw new Error('Camera does not provide native snapshots. Please install the Snapshot Plugin.');
const takePicture = (request: SnapshotRequest) => {
if (pendingPicture)
return pendingPicture;
if (device.interfaces.includes(ScryptedInterface.Camera)) {
pendingPicture = device.takePicture({
picture: {
width: request.width,
height: request.height,
}
})
.then(media => mediaManager.convertMediaObjectToBuffer(media, 'image/jpeg'));
}
else {
pendingPicture = Promise.reject(new Error('Camera does not provide native snapshots. Please install the Snapshot Plugin.'));
}
pendingPicture
.finally(() => {
pendingPicture = undefined;
})
return pendingPicture;
}
const throttledTakePicture = throttle(takePicture, 9000, {
leading: true,
trailing: true,
});
function snapshotAll(request: SnapshotRequest) {
for (const snapshotThrottle of homekitPlugin.snapshotThrottles.values()) {
snapshotThrottle(request);
}
const media = await device.takePicture({
reason: request.reason === ResourceRequestReason.EVENT ? 'event' : 'periodic',
picture: {
width: request.width,
height: request.height,
}
})
return await mediaManager.convertMediaObjectToBuffer(media, 'image/jpeg');
}
homekitPlugin.snapshotThrottles.set(device.id, takePicture);
@@ -72,11 +49,7 @@ export function createSnapshotHandler(device: ScryptedDevice & VideoCamera & Cam
// this call is not a bug, to force lodash to take a picture on the trailing edge,
// throttle must be called twice.
// no longer necessary in accessory mode?
snapshotAll(request);
snapshotAll(request);
callback(null, await throttledTakePicture(request));
callback(null, await takePicture(request));
}
catch (e) {
console.error('snapshot error', e);

View File

@@ -1,24 +1,22 @@
{
"name": "@scrypted/objectdetector",
"version": "0.1.19",
"version": "0.1.21",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/objectdetector",
"version": "0.1.19",
"version": "0.1.21",
"license": "Apache-2.0",
"dependencies": {
"@scrypted/common": "file:../../common",
"@scrypted/sdk": "file:../../sdk",
"lodash": "^4.17.21",
"polygon-clipping": "^0.15.3",
"semver": "^7.3.8"
"polygon-clipping": "^0.15.7",
"semver": "^7.5.4"
},
"devDependencies": {
"@types/lodash": "^4.14.175",
"@types/node": "^14.17.11",
"@types/semver": "^7.3.13"
"@types/node": "^20.11.0",
"@types/semver": "^7.5.6"
}
},
"../../common": {
@@ -28,22 +26,22 @@
"dependencies": {
"@scrypted/sdk": "file:../sdk",
"@scrypted/server": "file:../server",
"http-auth-utils": "^3.0.2",
"node-fetch-commonjs": "^3.1.1",
"typescript": "^4.4.3"
"http-auth-utils": "^5.0.1",
"typescript": "^5.3.3"
},
"devDependencies": {
"@types/node": "^16.9.0"
"@types/node": "^20.11.0",
"ts-node": "^10.9.2"
}
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.3.2",
"version": "0.3.4",
"license": "ISC",
"dependencies": {
"@babel/preset-typescript": "^7.18.6",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"axios": "^1.6.5",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.15.9",
@@ -81,29 +79,21 @@
"resolved": "../../sdk",
"link": true
},
"node_modules/@types/lodash": {
"version": "4.14.177",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.177.tgz",
"integrity": "sha512-0fDwydE2clKe9MNfvXHBHF9WEahRuj+msTuQqOmAApNORFvhMYZKNGGJdCzuhheVjMps/ti0Ak/iJPACMaevvw==",
"dev": true
},
"node_modules/@types/node": {
"version": "14.17.33",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.33.tgz",
"integrity": "sha512-noEeJ06zbn3lOh4gqe2v7NMGS33jrulfNqYFDjjEbhpDEHR5VTxgYNQSBqBlJIsBJW3uEYDgD6kvMnrrhGzq8g==",
"dev": true
"version": "20.11.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.0.tgz",
"integrity": "sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/semver": {
"version": "7.3.13",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
"integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
"version": "7.5.6",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz",
"integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==",
"dev": true
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -116,13 +106,19 @@
}
},
"node_modules/polygon-clipping": {
"version": "0.15.3",
"resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.3.tgz",
"integrity": "sha512-ho0Xx5DLkgxRx/+n4O74XyJ67DcyN3Tu9bGYKsnTukGAW6ssnuak6Mwcyb1wHy9MZc9xsUWqIoiazkZB5weECg==",
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.7.tgz",
"integrity": "sha512-nhfdr83ECBg6xtqOAJab1tbksbBAOMUltN60bU+llHVOL0e5Onm1WpAXXWXVB39L8AJFssoIhEVuy/S90MmotA==",
"dependencies": {
"robust-predicates": "^3.0.2",
"splaytree": "^3.1.0"
}
},
"node_modules/robust-predicates": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
},
"node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
@@ -142,6 +138,12 @@
"resolved": "https://registry.npmjs.org/splaytree/-/splaytree-3.1.2.tgz",
"integrity": "sha512-4OM2BJgC5UzrhVnnJA4BkHKGtjXNzzUfpQjCO8I05xYPsfS/VuQDwjCGGMi8rYQilHEV4j8NBqTFbls/PZEE7A=="
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
@@ -171,10 +173,10 @@
"requires": {
"@scrypted/sdk": "file:../sdk",
"@scrypted/server": "file:../server",
"@types/node": "^16.9.0",
"http-auth-utils": "^3.0.2",
"node-fetch-commonjs": "^3.1.1",
"typescript": "^4.4.3"
"@types/node": "^20.11.0",
"http-auth-utils": "^5.0.1",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
}
},
"@scrypted/sdk": {
@@ -184,7 +186,7 @@
"@types/node": "^18.11.18",
"@types/stringify-object": "^4.0.0",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"axios": "^1.6.5",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.15.9",
@@ -201,29 +203,21 @@
"webpack-bundle-analyzer": "^4.5.0"
}
},
"@types/lodash": {
"version": "4.14.177",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.177.tgz",
"integrity": "sha512-0fDwydE2clKe9MNfvXHBHF9WEahRuj+msTuQqOmAApNORFvhMYZKNGGJdCzuhheVjMps/ti0Ak/iJPACMaevvw==",
"dev": true
},
"@types/node": {
"version": "14.17.33",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.33.tgz",
"integrity": "sha512-noEeJ06zbn3lOh4gqe2v7NMGS33jrulfNqYFDjjEbhpDEHR5VTxgYNQSBqBlJIsBJW3uEYDgD6kvMnrrhGzq8g==",
"dev": true
"version": "20.11.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.0.tgz",
"integrity": "sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==",
"dev": true,
"requires": {
"undici-types": "~5.26.4"
}
},
"@types/semver": {
"version": "7.3.13",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
"integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
"version": "7.5.6",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz",
"integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==",
"dev": true
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -233,13 +227,19 @@
}
},
"polygon-clipping": {
"version": "0.15.3",
"resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.3.tgz",
"integrity": "sha512-ho0Xx5DLkgxRx/+n4O74XyJ67DcyN3Tu9bGYKsnTukGAW6ssnuak6Mwcyb1wHy9MZc9xsUWqIoiazkZB5weECg==",
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.7.tgz",
"integrity": "sha512-nhfdr83ECBg6xtqOAJab1tbksbBAOMUltN60bU+llHVOL0e5Onm1WpAXXWXVB39L8AJFssoIhEVuy/S90MmotA==",
"requires": {
"robust-predicates": "^3.0.2",
"splaytree": "^3.1.0"
}
},
"robust-predicates": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
},
"semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
@@ -253,6 +253,12 @@
"resolved": "https://registry.npmjs.org/splaytree/-/splaytree-3.1.2.tgz",
"integrity": "sha512-4OM2BJgC5UzrhVnnJA4BkHKGtjXNzzUfpQjCO8I05xYPsfS/VuQDwjCGGMi8rYQilHEV4j8NBqTFbls/PZEE7A=="
},
"undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/objectdetector",
"version": "0.1.19",
"version": "0.1.21",
"description": "Scrypted Video Analysis Plugin. Installed alongside a detection service like OpenCV or TensorFlow.",
"author": "Scrypted",
"license": "Apache-2.0",
@@ -45,13 +45,11 @@
"dependencies": {
"@scrypted/common": "file:../../common",
"@scrypted/sdk": "file:../../sdk",
"lodash": "^4.17.21",
"polygon-clipping": "^0.15.3",
"semver": "^7.3.8"
"polygon-clipping": "^0.15.7",
"semver": "^7.5.4"
},
"devDependencies": {
"@types/lodash": "^4.14.175",
"@types/node": "^14.17.11",
"@types/semver": "^7.3.13"
"@types/node": "^20.11.0",
"@types/semver": "^7.5.6"
}
}

View File

@@ -0,0 +1,36 @@
import { CpuInfo, cpus } from 'os';
function getIdleTotal(cpu: CpuInfo) {
const t = cpu.times;
const total = t.user + t.nice + t.sys + t.idle + t.irq;
const idle = t.idle;
return {
idle,
total,
}
}
export class CpuTimer {
previousSample: ReturnType<typeof cpus>;
sample(): number {
const sample = cpus();
const previousSample = this.previousSample;
this.previousSample = sample;
// can cpu count change at runtime, who knows
if (!previousSample || previousSample.length !== sample.length)
return 0;
const times = sample.map((v, i) => {
const c = getIdleTotal(v);
const p = getIdleTotal(previousSample[i]);
const total = c.total - p.total;
const idle = c.idle - p.idle;
return 1 - idle / total;
});
const total = times.reduce((p, c) => p + c, 0);
return total / sample.length;
}
}

View File

@@ -5,10 +5,9 @@ import { StorageSettings } from '@scrypted/sdk/storage-settings';
import crypto from 'crypto';
import { AutoenableMixinProvider } from "../../../common/src/autoenable-mixin-provider";
import { SettingsMixinDeviceBase } from "../../../common/src/settings-mixin";
import { CpuTimer } from './cpu-timer';
import { FFmpegVideoFrameGenerator } from './ffmpeg-videoframes';
import { getMaxConcurrentObjectDetectionSessions } from './performance-profile';
import { insidePolygon, normalizeBox, polygonOverlap } from './polygon';
import { serverSupportsMixinEventMasking } from './server-version';
import { SMART_MOTIONSENSOR_PREFIX, SmartMotionSensor, createObjectDetectorStorageSetting } from './smart-motionsensor';
import { getAllDevices, safeParseJson } from './util';
@@ -125,11 +124,14 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
this.bindObjectDetection();
this.register();
this.detectionIntervalTimeout = setInterval(async () => {
if (this.released)
return;
this.maybeStartDetection();
}, 60000);
// ensure motion sensors stay alive. plugin will manage object detection throttling.
if (this.hasMotionType) {
this.detectionIntervalTimeout = setInterval(async () => {
if (this.released)
return;
this.maybeStartDetection();
}, 60000);
}
this.storageSettings.settings.zones.mapGet = () => Object.keys(this.zones);
this.storageSettings.settings.zones.onGet = async () => {
@@ -187,8 +189,10 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
maybeStartDetection() {
if (!this.hasMotionType) {
// object detection may be restarted if there are slots available.
if (this.cameraDevice.motionDetected && this.plugin.canStartObjectDetection(this))
if (this.cameraDevice.motionDetected && this.plugin.canStartObjectDetection(this)) {
this.startPipelineAnalysis();
return true;
}
return;
}
@@ -238,7 +242,7 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
return;
}
this.startPipelineAnalysis();
this.maybeStartDetection();
});
return;
@@ -510,10 +514,14 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
if (!o.boundingBox)
continue;
o.zones = []
const box = normalizeBox(o.boundingBox, detection.inputDimensions);
let included: boolean;
// need a way to explicitly include package zone.
if (o.zones)
included = true;
else
o.zones = [];
for (const [zone, zoneValue] of Object.entries(this.zones)) {
if (zoneValue.length < 3) {
// this.console.warn(zone, 'Zone is unconfigured, skipping.');
@@ -627,7 +635,7 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
}
get motionSensorSupplementation() {
if (!serverSupportsMixinEventMasking() || !this.interfaces.includes(ScryptedInterface.MotionSensor))
if (!this.interfaces.includes(ScryptedInterface.MotionSensor))
return BUILTIN_MOTION_SENSOR_REPLACE;
const supp = this.storage.getItem('motionSensorSupplementation');
@@ -934,19 +942,6 @@ export class ObjectDetectionPlugin extends AutoenableMixinProvider implements Se
statsSnapshotDetections: number;
statsSnapshotConcurrent = 0;
storageSettings = new StorageSettings(this, {
maxConcurrentDetections: {
title: 'Max Concurrent Detections',
description: `The max number concurrent cameras that will perform object detection while their motion sensor is triggered. Older sessions will be terminated when the limit is reached. The default value is ${getMaxConcurrentObjectDetectionSessions()}.`,
defaultValue: 'Default',
combobox: true,
choices: [
'Default',
...[2, 3, 4, 5, 6, 7, 8, 9, 10].map(i => i.toString()),
],
mapPut: (o, v) => {
return parseInt(v) || 'Default';
}
},
activeMotionDetections: {
title: 'Active Motion Detection Sessions',
multiple: true,
@@ -1010,6 +1005,8 @@ export class ObjectDetectionPlugin extends AutoenableMixinProvider implements Se
},
});
devices = new Map<string, any>();
cpuTimer = new CpuTimer();
cpuUsage = 0;
pruneOldStatistics() {
const now = Date.now();
@@ -1024,69 +1021,53 @@ export class ObjectDetectionPlugin extends AutoenableMixinProvider implements Se
this.statsSnapshotDetections++;
}
get maxConcurrent() {
let maxConcurrent = this.storageSettings.values.maxConcurrentDetections || 'Default';
maxConcurrent = Math.max(parseInt(maxConcurrent)) || getMaxConcurrentObjectDetectionSessions();
return maxConcurrent;
}
canStartObjectDetection(mixin: ObjectDetectionMixin) {
const maxConcurrent = this.maxConcurrent;
const runningDetections = [...this.currentMixins.values()]
.map(d => [...d.currentMixins.values()].filter(dd => !dd.hasMotionType)).flat()
.filter(c => c.detectorRunning)
.sort((a, b) => a.detectionStartTime - b.detectionStartTime);
const runningDetections = this.runningObjectDetections;
// already running
if (runningDetections.find(o => o.id === mixin.id))
return false;
if (runningDetections.length < maxConcurrent)
if (!runningDetections.length)
return true;
const [first] = runningDetections;
if (Date.now() - first.detectionStartTime > 30000)
return true;
const cpuPerDetector = this.cpuUsage / runningDetections.length;
const cpuPercent = Math.round(this.cpuUsage * 100);
if (cpuPerDetector * (runningDetections.length + 1) > .9) {
const [first] = runningDetections;
if (Date.now() - first.detectionStartTime > 30000) {
first.console.warn(`CPU is at capacity: ${cpuPercent} with ${runningDetections.length} cameras. Ending object detection to process activity on ${mixin.name}.`);
first.endObjectDetection();
mixin.console.warn(`CPU is at capacity: ${cpuPercent} with ${runningDetections.length} cameras. Ending object detection on ${first.name} to process activity.`);
return true;
}
mixin.console.log(`Not starting object detection to continue processing recent activity on ${first.name}.`);
return false;
mixin.console.warn(`CPU is at capacity: ${cpuPercent} with ${runningDetections.length} cameras. Not starting object detection to continue processing recent activity on ${first.name}.`);
return false;
}
// CPU capacity is fine
return true;
}
get runningObjectDetections() {
const runningDetections = [...this.currentMixins.values()]
.map(d => [...d.currentMixins.values()].filter(dd => !dd.hasMotionType)).flat()
.filter(c => c.detectorRunning)
.sort((a, b) => a.detectionStartTime - b.detectionStartTime);
return runningDetections;
}
objectDetectionStarted(name: string, console: Console) {
this.resetStats(console);
this.statsSnapshotConcurrent++;
const maxConcurrent = this.maxConcurrent;
const objectDetections = [...this.currentMixins.values()]
.map(d => [...d.currentMixins.values()].filter(dd => !dd.hasMotionType)).flat()
.filter(c => c.detectorRunning)
.sort((a, b) => a.detectionStartTime - b.detectionStartTime);
while (objectDetections.length > maxConcurrent) {
const old = objectDetections.shift();
// allow exceeding the concurrency limit if user interaction triggered analyze.
if (old.analyzeStop)
continue;
old.console.log(`Ending object detection to process activity on ${name}.`);
old.endObjectDetection();
}
}
objectDetectionEnded(console: Console) {
this.resetStats(console);
this.statsSnapshotConcurrent--;
const objectDetections = [...this.currentMixins.values()]
.map(d => [...d.currentMixins.values()].filter(dd => !dd.hasMotionType)).flat()
.filter(c => !c.detectorRunning);
for (const notRunning of objectDetections) {
notRunning.maybeStartDetection();
}
}
resetStats(console: Console) {
@@ -1126,7 +1107,34 @@ export class ObjectDetectionPlugin extends AutoenableMixinProvider implements Se
],
nativeId: 'ffmpeg',
})
})
});
setInterval(() => {
this.cpuUsage = this.cpuTimer.sample();
// this.console.log('cpu usage', Math.round(this.cpuUsage * 100));
const runningDetections = this.runningObjectDetections;
let allowStart = 1;
if (runningDetections.length) {
const cpuPerDetector = this.cpuUsage / runningDetections.length;
allowStart = Math.ceil(1 / cpuPerDetector) - runningDetections.length;
if (allowStart <= 0)
return;
}
const idleDetectors = [...this.currentMixins.values()]
.map(d => [...d.currentMixins.values()].filter(dd => !dd.hasMotionType)).flat()
.filter(c => !c.detectorRunning);
for (const notRunning of idleDetectors) {
if (notRunning.maybeStartDetection()) {
allowStart--;
if (allowStart <= 0)
return;
}
}
}, 10000)
}
async getDevice(nativeId: string): Promise<any> {

View File

@@ -1,32 +0,0 @@
import os from 'os';
let totalGigahertz = 0;
export function getMaxConcurrentObjectDetectionSessions() {
const cpus = os.cpus();
// apple silicon cpu.speed is incorrect, and can handle quite a bit due to
// gpu decode and neural core usage.
// .5 detect per cpu is a conservative guess. so an m1 ultra would handle 10
// simultaneous camera detections.
// apple silicon also reports cpu speed as 24 mhz, so the following code would
// fail anyways.
if (process.platform === 'darwin' && process.arch === 'arm64')
return cpus.length * .5;
let speed = 0;
for (const cpu of cpus) {
// can cpu speed be zero? is that a thing?
speed += cpu.speed || 600;
}
totalGigahertz = Math.max(speed, totalGigahertz);
// a wyse 5070 self reports in description as 1.5ghz and has 4 cores and can comfortably handle
// two 2k detections at the same time.
// the speed reported while detecting caps at 2500, presumably due to burst?
// the total mhz would be 10000 in this case.
// observed idle per cpu speed is 800.
// not sure how hyperthreading plays into this.
return Math.max(2, Math.round(totalGigahertz / 4000));
}

View File

@@ -1,13 +0,0 @@
import semver from 'semver';
import sdk from '@scrypted/sdk';
export function serverSupportsMixinEventMasking() {
try {
if (!sdk.serverVersion)
return false;
return semver.gte(sdk.serverVersion, '0.5.0');
}
catch (e) {
}
return false;
}

View File

@@ -1,26 +1,25 @@
{
"name": "@scrypted/onvif",
"version": "0.1.5",
"version": "0.1.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@scrypted/onvif",
"version": "0.1.5",
"version": "0.1.8",
"license": "Apache",
"dependencies": {
"@scrypted/common": "file:../../common",
"@scrypted/sdk": "file:../../sdk",
"base-64": "^1.0.0",
"http-auth-utils": "^4.0.0",
"md5": "^2.3.0",
"onvif": "file:./onvif",
"xml2js": "^0.6.0"
"xml2js": "^0.6.2"
},
"devDependencies": {
"@types/md5": "^2.3.2",
"@types/node": "^20.3.2",
"@types/xml2js": "^0.4.11"
"@types/md5": "^2.3.5",
"@types/node": "^20.11.0",
"@types/xml2js": "^0.4.14"
}
},
"../../common": {
@@ -30,7 +29,7 @@
"dependencies": {
"@scrypted/sdk": "file:../sdk",
"@scrypted/server": "file:../server",
"http-auth-utils": "^3.0.2",
"http-auth-utils": "^5.0.1",
"node-fetch-commonjs": "^3.1.1",
"typescript": "^5.3.3"
},
@@ -46,7 +45,7 @@
"dependencies": {
"@babel/preset-typescript": "^7.18.6",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"axios": "^1.6.5",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.15.9",
@@ -828,24 +827,24 @@
"link": true
},
"node_modules/@types/md5": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/@types/md5/-/md5-2.3.2.tgz",
"integrity": "sha512-v+JFDu96+UYJ3/UWzB0mEglIS//MZXgRaJ4ubUPwOM0gvLc/kcQ3TWNYwENEK7/EcXGQVrW8h/XqednSjBd/Og==",
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/@types/md5/-/md5-2.3.5.tgz",
"integrity": "sha512-/i42wjYNgE6wf0j2bcTX6kuowmdL/6PE4IVitMpm2eYKBUuYCprdcWVK+xEF0gcV6ufMCRhtxmReGfc6hIK7Jw==",
"dev": true
},
"node_modules/@types/node": {
"version": "20.10.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz",
"integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==",
"version": "20.11.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.0.tgz",
"integrity": "sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/xml2js": {
"version": "0.4.11",
"resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.11.tgz",
"integrity": "sha512-JdigeAKmCyoJUiQljjr7tQG3if9NkqGUgwEUqBvV0N7LM4HyQk7UXCnusRa1lnvXAEYJ8mw8GtZWioagNztOwA==",
"version": "0.4.14",
"resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz",
"integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==",
"dev": true,
"dependencies": {
"@types/node": "*"
@@ -2123,17 +2122,6 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
"node_modules/http-auth-utils": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/http-auth-utils/-/http-auth-utils-4.0.0.tgz",
"integrity": "sha512-jdX1sda7HuAOmCqHJ4FoYFEO6P3x9PQosEyZMqpi3EUsB/HxnoqauRThLelBTeOs+osAu8Y3VTdo4G4fdXk2Ig==",
"dependencies": {
"yerror": "^6.2.1"
},
"engines": {
"node": ">=16.15.0"
}
},
"node_modules/http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@@ -4119,14 +4107,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/yerror": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/yerror/-/yerror-6.2.1.tgz",
"integrity": "sha512-WPZgybhCBzsMSSqGYBnj20NZo4FiFKQG0+i/21cYGVd4B7eYtvYDOpjk/0e8UM1eVHJ+4Ja6bZ7TAjHH6mk+ew==",
"engines": {
"node": ">=12.19.0"
}
},
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/onvif",
"version": "0.1.5",
"version": "0.1.8",
"description": "ONVIF Camera Plugin for Scrypted",
"author": "Scrypted",
"license": "Apache",
@@ -39,14 +39,13 @@
"@scrypted/common": "file:../../common",
"@scrypted/sdk": "file:../../sdk",
"base-64": "^1.0.0",
"http-auth-utils": "^4.0.0",
"md5": "^2.3.0",
"onvif": "file:./onvif",
"xml2js": "^0.6.0"
"xml2js": "^0.6.2"
},
"devDependencies": {
"@types/md5": "^2.3.2",
"@types/node": "^20.3.2",
"@types/xml2js": "^0.4.11"
"@types/md5": "^2.3.5",
"@types/node": "^20.11.0",
"@types/xml2js": "^0.4.14"
}
}

View File

@@ -492,7 +492,7 @@ class OnvifProvider extends RtspProvider implements DeviceDiscovery {
const username = settings.username?.toString();
const password = settings.password?.toString();
const skipValidate = settings.skipValidate === 'true';
const skipValidate = settings.skipValidate?.toString() === 'true';
let ptzCapabilities: string[];
if (!skipValidate) {
try {

View File

@@ -1,12 +1,7 @@
import { AuthFetchCredentialState, AuthFetchOptions, authHttpFetch } from '@scrypted/common/src/http-auth-fetch';
import { HttpFetchOptions, HttpFetchResponseType, AuthFetchCredentialState, authHttpFetch } from '@scrypted/common/src/http-auth-fetch';
import { EventEmitter } from 'events';
import https, { RequestOptions } from 'https';
import https from 'https';
import { Readable } from 'stream';
import { FetchParser, HttpFetchOptions, HttpFetchResponseType } from '../../../server/src/http-fetch-helpers';
const httpsAgent = new https.Agent({
rejectUnauthorized: false,
})
const onvif = require('onvif');
const { Cam } = onvif;
@@ -71,17 +66,15 @@ export class OnvifCameraAPI {
};
}
async request<T extends HttpFetchResponseType>(urlOrOptions: string | URL | HttpFetchOptions<T>, body?: Readable) {
const options: AuthFetchOptions<T> = {
async request(urlOrOptions: string | URL | HttpFetchOptions<Readable>, body?: Readable) {
const response = await authHttpFetch({
...typeof urlOrOptions !== 'string' && !(urlOrOptions instanceof URL) ? urlOrOptions : {
url: urlOrOptions,
},
rejectUnauthorized: false,
credential: this.credential,
body,
};
const response = await authHttpFetch(options);
});
return response;
}

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/openvino",
"version": "0.1.48",
"version": "0.1.51",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/openvino",
"version": "0.1.48",
"version": "0.1.51",
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
}

View File

@@ -41,5 +41,5 @@
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
},
"version": "0.1.48"
"version": "0.1.51"
}

View File

@@ -1,4 +1,4 @@
openvino==2023.2.0
openvino==2023.3.0
# pillow-simd is available on x64 linux
# pillow-simd confirmed not building with arm64 linux or apple silicon

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/prebuffer-mixin",
"version": "0.10.11",
"version": "0.10.12",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/prebuffer-mixin",
"version": "0.10.11",
"version": "0.10.12",
"license": "Apache-2.0",
"dependencies": {
"@scrypted/common": "file:../../common",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/prebuffer-mixin",
"version": "0.10.11",
"version": "0.10.12",
"description": "Video Stream Rebroadcast, Prebuffer, and Management Plugin for Scrypted.",
"author": "Scrypted",
"license": "Apache-2.0",

View File

@@ -1,4 +1,4 @@
import path from 'path'
import { AutoenableMixinProvider } from '@scrypted/common/src/autoenable-mixin-provider';
import { getDebugModeH264EncoderArgs, getH264EncoderArgs } from '@scrypted/common/src/ffmpeg-hardware-acceleration';
import { addVideoFilterArguments } from '@scrypted/common/src/ffmpeg-helpers';
@@ -971,6 +971,7 @@ class PrebufferSession {
const clientPromise = await listenSingleRtspClient({
hostname,
pathToken: path.join(crypto.randomBytes(8).toString('hex'), this.mixin.id),
createServer: duplex => {
sdp = addTrackControls(sdp);
server = new FileRtspServer(duplex, sdp);

View File

@@ -10,7 +10,7 @@
"port": 10081,
"request": "attach",
"skipFiles": [
"**/plugin-remote-worker.*",
"**/plugin-console.*",
"<node_internals>/**"
],
"preLaunchTask": "scrypted: deploy+debug",

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/reolink",
"version": "0.0.57",
"version": "0.0.58",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@scrypted/reolink",
"version": "0.0.57",
"version": "0.0.58",
"license": "Apache",
"dependencies": {
"@scrypted/common": "file:../../common",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/reolink",
"version": "0.0.57",
"version": "0.0.58",
"description": "Reolink Plugin for Scrypted",
"author": "Scrypted",
"license": "Apache",

View File

@@ -2,7 +2,7 @@ import { AuthFetchCredentialState, AuthFetchOptions, authHttpFetch } from '@scry
import { EventEmitter } from 'events';
import https, { RequestOptions } from 'https';
import { PassThrough, Readable } from 'stream';
import { HttpFetchOptions, HttpFetchResponseType } from '../../../server/src/http-fetch-helpers';
import { HttpFetchOptions, HttpFetchResponseType } from '../../../server/src/fetch/http-fetch';
import { getMotionState, reolinkHttpsAgent } from './probe';
import { PanTiltZoomCommand } from "@scrypted/sdk";
@@ -69,17 +69,15 @@ export class ReolinkCameraClient {
};
}
async request<T extends HttpFetchResponseType>(urlOrOptions: string | URL | HttpFetchOptions<T>, body?: Readable) {
const options: AuthFetchOptions<T> = {
async request(urlOrOptions: string | URL | HttpFetchOptions<Readable>, body?: Readable) {
const response = await authHttpFetch({
...typeof urlOrOptions !== 'string' && !(urlOrOptions instanceof URL) ? urlOrOptions : {
url: urlOrOptions,
},
rejectUnauthorized: false,
credential: this.credential,
body,
};
const response = await authHttpFetch(options);
});
return response;
}

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/snapshot",
"version": "0.2.31",
"version": "0.2.34",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/snapshot",
"version": "0.2.31",
"version": "0.2.34",
"dependencies": {
"@types/node": "^20.10.6",
"sharp": "^0.33.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/snapshot",
"version": "0.2.31",
"version": "0.2.34",
"description": "Snapshot Plugin for Scrypted",
"scripts": {
"scrypted-setup-project": "scrypted-setup-project",

View File

@@ -1,6 +1,6 @@
import { AutoenableMixinProvider } from "@scrypted/common/src/autoenable-mixin-provider";
import { AuthFetchCredentialState, authHttpFetch } from '@scrypted/common/src/http-auth-fetch';
import { RefreshPromise, TimeoutError, createMapPromiseDebouncer, singletonPromise } from "@scrypted/common/src/promise-utils";
import { RefreshPromise, TimeoutError, createMapPromiseDebouncer, singletonPromise, timeoutPromise } from "@scrypted/common/src/promise-utils";
import { SettingsMixinDeviceBase, SettingsMixinDeviceOptions } from "@scrypted/common/src/settings-mixin";
import sdk, { BufferConverter, Camera, DeviceManifest, DeviceProvider, FFmpegInput, HttpRequest, HttpRequestHandler, HttpResponse, MediaObject, MediaObjectOptions, MixinProvider, RequestMediaStreamOptions, RequestPictureOptions, ResponsePictureOptions, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, SettingValue, Settings, VideoCamera } from "@scrypted/sdk";
import { StorageSettings } from "@scrypted/sdk/storage-settings";
@@ -8,7 +8,6 @@ import https from 'https';
import os from 'os';
import path from 'path';
import url from 'url';
import { BufferParser } from "../../../server/src/http-fetch-helpers";
import { ffmpegFilterImage, ffmpegFilterImageBuffer } from './ffmpeg-image-filter';
import { ImageConverter, ImageConverterNativeId } from './image-converter';
import { ImageReader, ImageReaderNativeId, loadSharp, loadVipsImage } from './image-reader';
@@ -220,22 +219,21 @@ class SnapshotMixin extends SettingsMixinDeviceBase<Camera> implements Camera {
let credential: AuthFetchCredentialState;
if (username && password) {
credential = {
username,
username,
password,
};
}
try {
const response = await authHttpFetch({
httpsAgent,
rejectUnauthorized: false,
url: this.storageSettings.values.snapshotUrl,
credential,
}, {
timeout: 60000,
headers: {
'Accept': 'image/*',
}
}, BufferParser);
},
});
return response.body;
}
@@ -271,9 +269,10 @@ class SnapshotMixin extends SettingsMixinDeviceBase<Camera> implements Camera {
async takePictureRaw(options?: RequestPictureOptions): Promise<Buffer> {
let picture: Buffer;
const eventSnapshot = options?.reason === 'event';
const periodicSnapshot = options?.reason === 'periodic';
try {
picture = await this.snapshotDebouncer({
const debounced = this.snapshotDebouncer({
id: options?.id,
reason: options?.reason,
}, eventSnapshot ? 0 : 2000, async () => {
@@ -285,6 +284,22 @@ class SnapshotMixin extends SettingsMixinDeviceBase<Camera> implements Camera {
this.lastAvailablePicture = picture;
return picture;
});
// periodic snapshot should get the immediately available picture.
// the debounce has already triggered a refresh for the next go around.
if (periodicSnapshot && this.currentPicture) {
const cp = this.currentPicture;
debounced.catch(() => {});
try {
picture = await timeoutPromise(1000, debounced);
}
catch (e) {
picture = cp;
}
}
else {
picture = await debounced;
}
}
catch (e) {
// use the fallback cached picture if it is somewhat recent.
@@ -664,6 +679,7 @@ export class SnapshotPlugin extends AutoenableMixinProvider implements MixinProv
const mixin = this.mixinDevices.get(id);
let buffer: Buffer;
const rpo: RequestPictureOptions = {
reason: search.get('reason') as 'event' | 'periodic',
picture: {
width: parseInt(search.get('width')) || undefined,
height: parseInt(search.get('height')) || undefined,

View File

@@ -1,30 +1,30 @@
{
"name": "@scrypted/synology-ss",
"version": "0.0.16",
"version": "0.0.17",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/synology-ss",
"version": "0.0.16",
"version": "0.0.17",
"license": "Apache",
"dependencies": {
"axios": "^0.24.0"
"axios": "^1.0.0"
},
"devDependencies": {
"@scrypted/sdk": "file:../../sdk",
"@types/node": "^16.6.1"
"@types/node": "^18.0.0"
}
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.2.86",
"version": "0.3.5",
"dev": true,
"license": "ISC",
"dependencies": {
"@babel/preset-typescript": "^7.18.6",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"axios": "^1.6.5",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.15.9",
@@ -59,23 +59,52 @@
"link": true
},
"node_modules/@types/node": {
"version": "16.11.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz",
"integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==",
"dev": true
"version": "18.19.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.10.tgz",
"integrity": "sha512-IZD8kAM02AW1HRDTPOlz3npFava678pr8Ie9Vp8uRhBROXAv8MXT2pCnGZZAKYdromsNQLHQcfWQ6EOatVLtqA==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
"integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
"dependencies": {
"follow-redirects": "^1.14.4"
"follow-redirects": "^1.15.4",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
"funding": [
{
"type": "individual",
@@ -90,6 +119,49 @@
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
}
},
"dependencies": {
@@ -100,7 +172,7 @@
"@types/node": "^18.11.18",
"@types/stringify-object": "^4.0.0",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"axios": "^1.6.5",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.15.9",
@@ -118,23 +190,80 @@
}
},
"@types/node": {
"version": "16.11.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz",
"integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==",
"dev": true
},
"axios": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
"version": "18.19.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.10.tgz",
"integrity": "sha512-IZD8kAM02AW1HRDTPOlz3npFava678pr8Ie9Vp8uRhBROXAv8MXT2pCnGZZAKYdromsNQLHQcfWQ6EOatVLtqA==",
"dev": true,
"requires": {
"follow-redirects": "^1.14.4"
"undici-types": "~5.26.4"
}
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"axios": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
"integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
"requires": {
"follow-redirects": "^1.15.4",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
},
"follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw=="
},
"form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
},
"mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"requires": {
"mime-db": "1.52.0"
}
},
"proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/synology-ss",
"version": "0.0.16",
"version": "0.0.17",
"description": "A Synology Surveillance Station plugin for Scrypted",
"author": "Scrypted",
"license": "Apache",
@@ -36,10 +36,10 @@
]
},
"dependencies": {
"axios": "^0.24.0"
"axios": "^1.0.0"
},
"devDependencies": {
"@scrypted/sdk": "file:../../sdk",
"@types/node": "^16.6.1"
"@types/node": "^18.0.0"
}
}

View File

@@ -164,11 +164,12 @@ export class SynologyApiClient {
const response = await this.client.get<SynologyApiResponse<T>>(url ?? await this.getApiPath(params.api), { params });
if (!response.data?.success) {
if (response.data?.error?.code) {
const errorCode = response.data?.error?.code;
if (errorCode) {
const errorCodeLookup = { ...errorCodeDescriptions, ...extraErrorCodes }
throw new Error(`${errorCodeLookup[response.data.error.code]} (error code ${response.data.error.code})`)
throw new SynologyApiError(`${errorCodeLookup[errorCode]} (error code ${errorCode})`, errorCode)
} else {
throw new Error(`Synology API call failed with status code ${response.status}`);
throw new SynologyApiError(`Synology API call failed with status code ${response.status}`);
}
}
@@ -186,7 +187,17 @@ export interface SynologyApiInfo {
maxVersion: number;
}
export interface SynologyApiError {
export class SynologyApiError extends Error {
code?: string;
constructor(message: string, code?: string) {
super(message);
this.name = 'SynologyApiError';
this.code = code;
}
}
export interface SynologyApiErrorObject {
code: string;
}
@@ -198,7 +209,7 @@ interface SynologyApiRequestParams {
interface SynologyApiResponse<T> {
data?: T;
error?: SynologyApiError;
error?: SynologyApiErrorObject;
success: boolean;
}

View File

@@ -1,6 +1,6 @@
import sdk, { Camera, Device, DeviceProvider, HttpRequest, HttpRequestHandler, HttpResponse, MediaObject, MediaStreamOptions, MediaStreamUrl, MotionSensor, PictureOptions, ResponseMediaStreamOptions, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, Settings, VideoCamera } from "@scrypted/sdk";
import { createInstanceableProviderPlugin, enableInstanceableProviderMode, isInstanceableProviderModeEnabled } from '../../../common/src/provider-plugin';
import { SynologyApiClient, SynologyCamera, SynologyCameraStream } from "./api/synology-api-client";
import { SynologyApiClient, SynologyApiError, SynologyCamera, SynologyCameraStream } from "./api/synology-api-client";
const { deviceManager } = sdk;
@@ -162,10 +162,11 @@ class SynologyCameraDevice extends ScryptedDeviceBase implements Camera, HttpReq
}
class SynologySurveillanceStation extends ScryptedDeviceBase implements Settings, DeviceProvider {
private cameras: SynologyCamera[];
private cameras: SynologyCamera[] = [];
private cameraDevices: Map<string, SynologyCameraDevice> = new Map();
api: SynologyApiClient;
private startup: Promise<void>;
private discovering: boolean;
constructor(nativeId?: string) {
super(nativeId);
@@ -177,66 +178,23 @@ class SynologySurveillanceStation extends ScryptedDeviceBase implements Settings
}
public async discoverDevices(duration: number): Promise<void> {
const url = this.getSetting('url');
const username = this.getSetting('username');
const password = this.getSetting('password');
const otpCode = this.getSetting('otpCode');
const mfaDeviceId = this.getSetting('mfaDeviceId');
if (this.discovering) return;
this.discovering = true;
this.log.clearAlerts();
if (!url) {
this.log.a('Must provide URL.');
return
}
if (!username) {
this.log.a('Must provide username.');
return
}
if (!password) {
this.log.a('Must provide password.');
return
}
if (!this.api || url !== this.api.url) {
this.api = new SynologyApiClient(url);
}
this.console.info(`Fetching list of cameras from Synology server...`);
try {
const newMfaDeviceId = await this.api.login(username, password, otpCode ? parseInt(otpCode) : undefined, !!otpCode, 'Scrypted', mfaDeviceId);
// If a OTP was present, store the device ID to allow us to skip the OTP requirement next login.
if (otpCode) {
this.storage.setItem('mfaDeviceId', newMfaDeviceId);
if (!await this.tryLogin()) {
return;
}
}
catch (e) {
this.log.a(`login error: ${e}`);
this.console.error('login error', e);
// Clear device ID upon login failure, since it's likely useless now
this.storage.removeItem('mfaDeviceId');
return;
}
finally {
// Clear the OTP setting if provided since it's a temporary code
if (otpCode) {
this.storage.removeItem('otpCode');
this.onDeviceEvent(ScryptedInterface.Settings, undefined);
}
}
try {
this.cameras = await this.api.listCameras();
if (!this.cameras) {
this.console.error('Cameras failed to load. Retrying in 10 seconds.');
setTimeout(() => {
this.discoverDevices(0);
}, 100000);
}, 10000);
return;
}
@@ -285,6 +243,8 @@ class SynologySurveillanceStation extends ScryptedDeviceBase implements Settings
catch (e) {
this.log.a(`device discovery error: ${e}`);
this.console.error('device discovery error', e);
} finally {
this.discovering = false;
}
}
@@ -353,7 +313,81 @@ class SynologySurveillanceStation extends ScryptedDeviceBase implements Settings
return;
}
this.storage.setItem(key, value.toString());
this.discoverDevices(0);
// Delaying discover in case user updated multiple settings, so that it doesn't run until all have been set
setTimeout(() => this.discoverDevices(0), 200);
}
private async tryLogin(): Promise<boolean> {
this.console.info('Logging into Synology...');
const url = this.getSetting('url');
const username = this.getSetting('username');
const password = this.getSetting('password');
const otpCode = this.getSetting('otpCode');
const mfaDeviceId = this.getSetting('mfaDeviceId');
this.log.clearAlerts();
if (!url) {
this.log.a('Must provide URL.');
return
}
if (!username) {
this.log.a('Must provide username.');
return
}
if (!password) {
this.log.a('Must provide password.');
return
}
if (!this.api || url !== this.api.url) {
this.api = new SynologyApiClient(url);
}
let successful = false;
for (let attempt=1; attempt<=3; attempt++) {
try {
const newMfaDeviceId = await this.api.login(username, password, otpCode ? parseInt(otpCode) : undefined, !!otpCode, 'Scrypted', mfaDeviceId);
// If a OTP was present, store the device ID to allow us to skip the OTP requirement next login.
if (otpCode) {
this.storage.setItem('mfaDeviceId', newMfaDeviceId);
}
successful = true;
}
catch (e) {
this.log.a(`login error on attempt ${attempt}: ${e}`);
this.console.error(`login error on attempt ${attempt}`, e);
if (e instanceof SynologyApiError) {
break;
} else {
// Retry on failures that aren't Synology-specific, such as timeouts
await new Promise((resolve) => setTimeout(resolve, attempt * 1000));
continue;
}
}
finally {
// Clear the OTP setting if provided since it's a temporary code
if (otpCode) {
this.storage.removeItem('otpCode');
this.onDeviceEvent(ScryptedInterface.Settings, undefined);
}
}
}
if (successful) {
this.console.info(`Successfully logged into Synology`);
} else {
this.console.info(`Failed to log into Synology`);
}
return successful;
}
}

View File

@@ -1,6 +1,6 @@
{
"compilerOptions": {
"module": "commonjs",
"module": "Node16",
"target": "ES2021",
"resolveJsonModule": true,
"moduleResolution": "Node16",

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/tensorflow-lite",
"version": "0.1.45",
"version": "0.1.46",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/tensorflow-lite",
"version": "0.1.45",
"version": "0.1.46",
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
}

View File

@@ -53,5 +53,5 @@
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
},
"version": "0.1.45"
"version": "0.1.46"
}

View File

@@ -70,7 +70,7 @@ class TensorFlowLitePlugin(
nonlocal model
if defaultModel:
model = "yolov8n_full_integer_quant_320"
model = "efficientdet_lite0_320_ptq"
self.yolo = "yolo" in model
self.yolov8 = "yolov8" in model

View File

@@ -1,4 +1,4 @@
{
"scrypted.debugHost": "koushik-ubuntu",
"scrypted.debugHost": "scrypted-server",
}

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/zwave",
"version": "0.1.2",
"version": "0.1.5",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/zwave",
"version": "0.1.2",
"version": "0.1.5",
"license": "Apache",
"dependencies": {
"@scrypted/sdk": "file:../../sdk",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/zwave",
"version": "0.1.2",
"version": "0.1.5",
"description": "Z-Wave USB Controller for Scrypted",
"author": "Scrypted",
"license": "Apache",

View File

@@ -0,0 +1,22 @@
import { CO2Sensor } from "@scrypted/sdk";
import type { ValueID } from "@zwave-js/core";
import { ZWaveNode, ZWaveNodeValueUpdatedArgs } from "zwave-js";
import { Notification } from "./Notification";
import { ZwaveDeviceBase } from "./ZwaveDeviceBase";
export class SmokeAlarmToCO2Sensor extends Notification implements CO2Sensor {
static getInterfaces(node: ZWaveNode, valueId: ValueID): string[] {
if (Notification.checkInterface(node, valueId, 'Smoke detected')
|| Notification.checkInterface(node, valueId, 'Smoke detected (location provided)')) {
return ['CO2Sensor'];
}
return null;
}
static onValueChanged(zwaveDevice: ZwaveDeviceBase, valueId: ZWaveNodeValueUpdatedArgs) {
if (valueId.propertyKey === 'Alarm status') {
const notification = Notification.lookupNotification(zwaveDevice, 'Smoke Alarm');
zwaveDevice.co2ppm = notification.lookupValue(valueId.newValue as number) ? 50 : 0;
}
}
}

View File

@@ -65,6 +65,7 @@ export class ZwaveDeviceBase extends ScryptedDeviceBase implements Refresh, Sett
}
onValueChanged(valueId: ZWaveNodeValueUpdatedArgs) {
this.console.log('value changed', valueId);
var cc = getCommandClassIndex(valueId.commandClass, valueId.property as number);
if (!cc) {
cc = getCommandClass(valueId.commandClass);

Some files were not shown because too many files have changed in this diff Show More