mirror of
https://github.com/koush/scrypted.git
synced 2026-02-03 22:23:27 +00:00
Compare commits
415 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8e94c0386 | ||
|
|
8c6e7b997a | ||
|
|
9abc7ca139 | ||
|
|
2a943eb5e0 | ||
|
|
a4fe78a48b | ||
|
|
50ff0833c9 | ||
|
|
c94085a6c7 | ||
|
|
c477437456 | ||
|
|
0da96130fe | ||
|
|
fdbf7ab60b | ||
|
|
0cecfb86ff | ||
|
|
9a195c6207 | ||
|
|
47021a7743 | ||
|
|
01400cf206 | ||
|
|
99da29a738 | ||
|
|
6378c5953a | ||
|
|
846034d7c8 | ||
|
|
ad47f14922 | ||
|
|
0066379b1e | ||
|
|
54193251ab | ||
|
|
cd5e169439 | ||
|
|
249a87bd4c | ||
|
|
803400c2d8 | ||
|
|
0e700a53d0 | ||
|
|
72647b0099 | ||
|
|
85e41180b2 | ||
|
|
18bf012bb5 | ||
|
|
3f2b8de169 | ||
|
|
c568c9a37d | ||
|
|
a831c48f5f | ||
|
|
f0357d45f2 | ||
|
|
479b3ce1f3 | ||
|
|
bd7c7de8a5 | ||
|
|
a06e786d19 | ||
|
|
dff05e733e | ||
|
|
76e34af149 | ||
|
|
4f2e9e88e1 | ||
|
|
2761b0745a | ||
|
|
ea78b7f59e | ||
|
|
b8d233f08d | ||
|
|
f4e93c82a2 | ||
|
|
c72c34f954 | ||
|
|
baec4e71da | ||
|
|
245c6e3006 | ||
|
|
566c18251c | ||
|
|
f18e58a108 | ||
|
|
5c6ea09d3e | ||
|
|
646a9f214a | ||
|
|
6506c4236f | ||
|
|
a3e27ce8f8 | ||
|
|
a54d5a5f59 | ||
|
|
7136759f8f | ||
|
|
049d9898b3 | ||
|
|
6d9e21b7b8 | ||
|
|
bfd1aef5d1 | ||
|
|
5f02e6a272 | ||
|
|
56bbf00edc | ||
|
|
0c66456a87 | ||
|
|
6b589b8d5a | ||
|
|
aef218c653 | ||
|
|
aea24a84f0 | ||
|
|
bab3bef0d1 | ||
|
|
56bda46ae9 | ||
|
|
368b0fc26a | ||
|
|
fa50d6faab | ||
|
|
115d168cd3 | ||
|
|
7ab93e8883 | ||
|
|
e25cf860f0 | ||
|
|
b5b56d81a8 | ||
|
|
e7b6cb021c | ||
|
|
e96f374432 | ||
|
|
c4126d7569 | ||
|
|
074ba733a3 | ||
|
|
622f494703 | ||
|
|
28c8b97c26 | ||
|
|
c0af17b38d | ||
|
|
44a82a1afa | ||
|
|
c9b4c14e35 | ||
|
|
76534f1368 | ||
|
|
3f9a863961 | ||
|
|
83b0ebd5f0 | ||
|
|
b923a4ea27 | ||
|
|
941d213087 | ||
|
|
0e11b8b4c5 | ||
|
|
1be14878d1 | ||
|
|
3e323911c7 | ||
|
|
ca36ab2d2d | ||
|
|
a80e95912e | ||
|
|
4432d8dd67 | ||
|
|
2d83d3ba97 | ||
|
|
1078faef62 | ||
|
|
47a981e15a | ||
|
|
e253cab555 | ||
|
|
80124ca83b | ||
|
|
f883d8738c | ||
|
|
bdca3b545c | ||
|
|
3ba02c44ab | ||
|
|
fe4733bb97 | ||
|
|
7ff893fbd3 | ||
|
|
e79c544690 | ||
|
|
13bf44ce50 | ||
|
|
544531122d | ||
|
|
778f0b7ad1 | ||
|
|
35e8a86593 | ||
|
|
c370773af4 | ||
|
|
184f293b92 | ||
|
|
6e10172f7e | ||
|
|
c5ae2cd539 | ||
|
|
e40566e89c | ||
|
|
59ccd4e4d8 | ||
|
|
ae80eb7727 | ||
|
|
f054172dcf | ||
|
|
0d7fb9e13c | ||
|
|
a526816b07 | ||
|
|
563e16b08f | ||
|
|
fd56990d64 | ||
|
|
d7aaf57e8f | ||
|
|
a2d50d54d5 | ||
|
|
1f86745252 | ||
|
|
1f4343ba2e | ||
|
|
3ad311898f | ||
|
|
799e5b53c7 | ||
|
|
833e5b34ab | ||
|
|
c99ac28e89 | ||
|
|
841475cb97 | ||
|
|
4b03a3a458 | ||
|
|
d686dd815c | ||
|
|
e0386a8922 | ||
|
|
9ef3478c88 | ||
|
|
690d160f33 | ||
|
|
59ff987bca | ||
|
|
1669f17c96 | ||
|
|
b0bfd4e05e | ||
|
|
7152671913 | ||
|
|
537c178699 | ||
|
|
77ecee110b | ||
|
|
29b163a7d8 | ||
|
|
5d74e80e90 | ||
|
|
764b6441d5 | ||
|
|
e2c43cb4ff | ||
|
|
7b6d094e8c | ||
|
|
3dfb2db02a | ||
|
|
e5a549db6a | ||
|
|
d500c815fe | ||
|
|
5f71c59b5a | ||
|
|
27407942a5 | ||
|
|
11b6963744 | ||
|
|
b9ee8866f0 | ||
|
|
bc80d31eaa | ||
|
|
327688232c | ||
|
|
2883a4ce46 | ||
|
|
1ad2fb915d | ||
|
|
fb701a32b7 | ||
|
|
7a8c661bb3 | ||
|
|
54d72fb371 | ||
|
|
e48812cec7 | ||
|
|
6c2db072c4 | ||
|
|
4bf2c0b614 | ||
|
|
a93cdb0ae4 | ||
|
|
ff85b7abc6 | ||
|
|
46dfb8d98e | ||
|
|
5240200f0f | ||
|
|
3bcb94fc6b | ||
|
|
a596bc712c | ||
|
|
f6d2dc456e | ||
|
|
441cce239e | ||
|
|
3016df32d1 | ||
|
|
5bd8ed0b1a | ||
|
|
79286a5138 | ||
|
|
8874e01072 | ||
|
|
0223a9f0f6 | ||
|
|
890c2667d0 | ||
|
|
ca14764e17 | ||
|
|
1030d7d03c | ||
|
|
2d40320868 | ||
|
|
3e32c3d019 | ||
|
|
1f9fa3966f | ||
|
|
c2d86237d6 | ||
|
|
5cfcfafc00 | ||
|
|
35b4028a47 | ||
|
|
bf6038a5d3 | ||
|
|
e1b2216543 | ||
|
|
89c1682421 | ||
|
|
5a4c527c59 | ||
|
|
9d9c10aa1e | ||
|
|
ccf20a5fca | ||
|
|
692e7964a7 | ||
|
|
57e38072b1 | ||
|
|
4e8e862482 | ||
|
|
eddcef8e54 | ||
|
|
09edc6d75e | ||
|
|
72c7c43d79 | ||
|
|
805f471ff9 | ||
|
|
6f797d53ec | ||
|
|
4903a0efcd | ||
|
|
36e3fcf429 | ||
|
|
78a126fe0a | ||
|
|
5029baf2d4 | ||
|
|
769bc014a8 | ||
|
|
096700486a | ||
|
|
b3a7d6be9c | ||
|
|
05751bce44 | ||
|
|
dced62a527 | ||
|
|
359f1cfc2f | ||
|
|
d4cae8abbb | ||
|
|
0e6b3346ed | ||
|
|
2409cc457c | ||
|
|
0b794aa381 | ||
|
|
98017a5aa6 | ||
|
|
f2e7cc4017 | ||
|
|
d7201a16a7 | ||
|
|
99d1dc7282 | ||
|
|
18ae09e41c | ||
|
|
2ebe774e59 | ||
|
|
b887b8a47c | ||
|
|
8e391dee2f | ||
|
|
469f693d58 | ||
|
|
1c96a7d492 | ||
|
|
3f1b45c435 | ||
|
|
4b715e55d2 | ||
|
|
75dc63acc3 | ||
|
|
6c79f42bb7 | ||
|
|
9d4f006caa | ||
|
|
05b206f897 | ||
|
|
1f22218b23 | ||
|
|
c9568df165 | ||
|
|
c98e91cd39 | ||
|
|
e3ecff04ce | ||
|
|
f9f50f34c3 | ||
|
|
cd298f7d76 | ||
|
|
c95248fce0 | ||
|
|
e50f3fa793 | ||
|
|
c74be7e90f | ||
|
|
4d288727ce | ||
|
|
1f19dc191d | ||
|
|
37d4e5be73 | ||
|
|
e64ec98211 | ||
|
|
8b6c0c4f7b | ||
|
|
3b16c68c75 | ||
|
|
67be05880c | ||
|
|
414a9403c2 | ||
|
|
053106415c | ||
|
|
f3690af92a | ||
|
|
c4cc12fdff | ||
|
|
58e8772f7c | ||
|
|
4ae9b72471 | ||
|
|
a8c64aa2d4 | ||
|
|
8ccbba485a | ||
|
|
2ec192e0fd | ||
|
|
e257953338 | ||
|
|
9e80eca8e1 | ||
|
|
172b32fd47 | ||
|
|
a6bf055b85 | ||
|
|
dab5be1103 | ||
|
|
126c489934 | ||
|
|
7f714b3d6a | ||
|
|
fde3c47d8c | ||
|
|
4b1623dfce | ||
|
|
1e62f7a418 | ||
|
|
83c9d9a4a6 | ||
|
|
b42afe0ca0 | ||
|
|
e8e5f9b33e | ||
|
|
15916d83b8 | ||
|
|
c1327974b2 | ||
|
|
33e2291912 | ||
|
|
2d2c5c436f | ||
|
|
8088ae20b1 | ||
|
|
4c658b8d99 | ||
|
|
aab78ec797 | ||
|
|
11ecff985d | ||
|
|
80a1a78a79 | ||
|
|
7875c51d62 | ||
|
|
b04aa75117 | ||
|
|
fc7d1eaf32 | ||
|
|
e5a7a55be8 | ||
|
|
fa9a2eb947 | ||
|
|
30891e0769 | ||
|
|
fb8256709a | ||
|
|
06d0a4a2f1 | ||
|
|
2fb6e0a368 | ||
|
|
c6ed0d8729 | ||
|
|
67c6f63dbe | ||
|
|
e62b4ad68b | ||
|
|
bfec5eb3f3 | ||
|
|
0f948ea672 | ||
|
|
a28a476d80 | ||
|
|
fdab50bf8e | ||
|
|
189be80a40 | ||
|
|
dae1b87825 | ||
|
|
8853ca2775 | ||
|
|
296652b550 | ||
|
|
fb2646a69f | ||
|
|
9f5787227b | ||
|
|
aedcc0709b | ||
|
|
756585ae95 | ||
|
|
4e8ee94012 | ||
|
|
5689792a77 | ||
|
|
ed40f29226 | ||
|
|
aad9a2123d | ||
|
|
602b5e4983 | ||
|
|
5f01cdc73b | ||
|
|
a1f82dd065 | ||
|
|
469305cc3b | ||
|
|
5ed4082918 | ||
|
|
1bfdacc476 | ||
|
|
2d03b55d8e | ||
|
|
a06894b165 | ||
|
|
e2c0b4d1bf | ||
|
|
501509dcd0 | ||
|
|
9cbc38173b | ||
|
|
8c7a4dc21e | ||
|
|
edfeacd075 | ||
|
|
20d1372d2a | ||
|
|
3999cb6696 | ||
|
|
167360a218 | ||
|
|
9b168bb012 | ||
|
|
31f2d33e57 | ||
|
|
513dd4867b | ||
|
|
08e723848f | ||
|
|
fb37061a04 | ||
|
|
56c6cb8947 | ||
|
|
4f38c6eea8 | ||
|
|
5eb2c586fa | ||
|
|
8fd89e75b4 | ||
|
|
10c9143333 | ||
|
|
eaeae02080 | ||
|
|
7460c714c1 | ||
|
|
d7874eb7a2 | ||
|
|
5847b585c7 | ||
|
|
901e0a2349 | ||
|
|
8c8c7934ff | ||
|
|
dd4efcd52f | ||
|
|
eab0746a0a | ||
|
|
385d331953 | ||
|
|
27a01a7df8 | ||
|
|
fc13a230d7 | ||
|
|
f7d88273e4 | ||
|
|
26124b7647 | ||
|
|
5ba30e6001 | ||
|
|
cfb78ebb7f | ||
|
|
83ad4ed7bc | ||
|
|
8612d8e1fb | ||
|
|
3aeddd0347 | ||
|
|
f41fa9055e | ||
|
|
6655cba3b6 | ||
|
|
4726630e29 | ||
|
|
4989aa621e | ||
|
|
772bfec55a | ||
|
|
cf9a0653f2 | ||
|
|
c5bbe5619e | ||
|
|
61be7fa58d | ||
|
|
8cb2e1516a | ||
|
|
1b15453997 | ||
|
|
7e65605ab8 | ||
|
|
793c583491 | ||
|
|
77d4b0a995 | ||
|
|
79eda5d356 | ||
|
|
86f3318133 | ||
|
|
1cf0327d2e | ||
|
|
3244956b91 | ||
|
|
6d5fedc931 | ||
|
|
7eca7f69c0 | ||
|
|
7dec399ed7 | ||
|
|
9edc63bd90 | ||
|
|
99d2f43699 | ||
|
|
ba1ecd54c5 | ||
|
|
e49f26b410 | ||
|
|
d7a417c984 | ||
|
|
1a7e0370c9 | ||
|
|
2fe4191f12 | ||
|
|
b2b5cde303 | ||
|
|
33b77b64de | ||
|
|
a41d4de97a | ||
|
|
cf367fa481 | ||
|
|
6f483f829b | ||
|
|
be69c25076 | ||
|
|
96d292d39f | ||
|
|
933c731fe6 | ||
|
|
aa2c1c65f9 | ||
|
|
5228dbff62 | ||
|
|
d3593b9e40 | ||
|
|
2ef482c47f | ||
|
|
52692c0912 | ||
|
|
98c901486a | ||
|
|
476bd3b427 | ||
|
|
f71826f6a1 | ||
|
|
ed72643d3e | ||
|
|
96219456f3 | ||
|
|
0e797c6ac6 | ||
|
|
672f01fd3f | ||
|
|
327acaec76 | ||
|
|
aac10c4f16 | ||
|
|
da4ba776f7 | ||
|
|
6cd0af492b | ||
|
|
6a2474d11e | ||
|
|
e8a5d5cfd3 | ||
|
|
f07604de4c | ||
|
|
ed35811296 | ||
|
|
ae2228f2e4 | ||
|
|
c92c8f2b52 | ||
|
|
478f1f4ad7 | ||
|
|
06c8b397f0 | ||
|
|
f8bcf196d3 | ||
|
|
d1b57ed3ad | ||
|
|
190914efd1 | ||
|
|
e26e53899e | ||
|
|
43c69914a4 | ||
|
|
73f859b1f6 | ||
|
|
a362b7d6d9 | ||
|
|
efa8515aa0 | ||
|
|
7987a78239 | ||
|
|
21752a3e7e | ||
|
|
b4d8f99cd5 | ||
|
|
6cf8f6db32 | ||
|
|
feb3b8f601 |
11
.github/ISSUE_TEMPLATE/bug_report.md
vendored
11
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -7,6 +7,17 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# Github Issues is not a Forum
|
||||
|
||||
**This issue tracker is not for hardware support or feature requests**. If you are troubleshooting adding a device for the first time, use Discord, Reddit, or Github Discussions. However, if something **was working**, and is now **no longer working**, you may create a Github issue.
|
||||
Created issues that do not meet these requirements or are improperly filled out will be immediately closed.
|
||||
|
||||
|
||||
# New Issue Instructions
|
||||
|
||||
1. Delete this section and everything above it.
|
||||
2. Fill out the sections below.
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is. The issue tracker is only for reporting bugs in Scrypted, for general support check Discord. Hardrware support requests or assistance requests will be immediately closed.
|
||||
|
||||
|
||||
20
.github/workflows/docker-common.yml
vendored
20
.github/workflows/docker-common.yml
vendored
@@ -10,9 +10,12 @@ jobs:
|
||||
# runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
NODE_VERSION: ["18", "20"]
|
||||
NODE_VERSION: [
|
||||
"18",
|
||||
# "20"
|
||||
]
|
||||
BASE: ["jammy"]
|
||||
FLAVOR: ["full", "lite", "thin"]
|
||||
FLAVOR: ["full", "lite"]
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3
|
||||
@@ -26,21 +29,13 @@ jobs:
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM64 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up SSH
|
||||
uses: MrSquaare/ssh-setup-action@v2
|
||||
with:
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM7 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
platforms: linux/arm64,linux/armhf
|
||||
platforms: linux/arm64
|
||||
append: |
|
||||
- endpoint: ssh://${{ secrets.DOCKER_SSH_USER }}@${{ secrets.DOCKER_SSH_HOST_ARM64 }}
|
||||
platforms: linux/arm64
|
||||
- endpoint: ssh://${{ secrets.DOCKER_SSH_USER }}@${{ secrets.DOCKER_SSH_HOST_ARM7 }}
|
||||
platforms: linux/armhf
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
@@ -63,9 +58,10 @@ jobs:
|
||||
BASE=${{ matrix.BASE }}
|
||||
context: install/docker/
|
||||
file: install/docker/Dockerfile.${{ matrix.FLAVOR }}
|
||||
platforms: linux/amd64,linux/armhf,linux/arm64
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
koush/scrypted-common:${{ matrix.NODE_VERSION }}-${{ matrix.BASE }}-${{ matrix.FLAVOR }}
|
||||
ghcr.io/koush/scrypted-common:${{ matrix.NODE_VERSION }}-${{ matrix.BASE }}-${{ matrix.FLAVOR }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
21
.github/workflows/docker.yml
vendored
21
.github/workflows/docker.yml
vendored
@@ -19,7 +19,14 @@ jobs:
|
||||
# runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
BASE: ["18-jammy-full", "18-jammy-lite", "18-jammy-thin", "20-jammy-full", "20-jammy-lite", "20-jammy-thin"]
|
||||
BASE: [
|
||||
"18-jammy-full",
|
||||
"18-jammy-lite",
|
||||
# "18-jammy-thin",
|
||||
# "20-jammy-full",
|
||||
# "20-jammy-lite",
|
||||
# "20-jammy-thin",
|
||||
]
|
||||
SUPERVISOR: ["", ".s6"]
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
@@ -45,21 +52,13 @@ jobs:
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM64 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up SSH
|
||||
uses: MrSquaare/ssh-setup-action@v2
|
||||
with:
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM7 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
platforms: linux/arm64,linux/armhf
|
||||
platforms: linux/arm64
|
||||
append: |
|
||||
- endpoint: ssh://${{ secrets.DOCKER_SSH_USER }}@${{ secrets.DOCKER_SSH_HOST_ARM64 }}
|
||||
platforms: linux/arm64
|
||||
- endpoint: ssh://${{ secrets.DOCKER_SSH_USER }}@${{ secrets.DOCKER_SSH_HOST_ARM7 }}
|
||||
platforms: linux/armhf
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
@@ -82,7 +81,7 @@ jobs:
|
||||
SCRYPTED_INSTALL_VERSION=${{ steps.package-version.outputs.NPM_VERSION }}
|
||||
context: install/docker/
|
||||
file: install/docker/Dockerfile${{ matrix.SUPERVISOR }}
|
||||
platforms: linux/amd64,linux/arm64,linux/armhf
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
${{ format('koush/scrypted:{0}{1}-v{2}', matrix.BASE, matrix.SUPERVISOR, github.event.inputs.publish_tag || steps.package-version.outputs.NPM_VERSION) }}
|
||||
|
||||
9
.gitmodules
vendored
9
.gitmodules
vendored
@@ -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
|
||||
@@ -35,3 +32,9 @@
|
||||
[submodule "plugins/cloud/node-nat-upnp"]
|
||||
path = plugins/cloud/external/node-nat-upnp
|
||||
url = ../../koush/node-nat-upnp.git
|
||||
[submodule "plugins/wyze/docker-wyze-bridge"]
|
||||
path = plugins/wyze/docker-wyze-bridge
|
||||
url = ../../koush/docker-wyze-bridge.git
|
||||
[submodule "plugins/onvif/onvif"]
|
||||
path = plugins/onvif/onvif
|
||||
url = ../../koush/onvif.git
|
||||
|
||||
22
common/.vscode/launch.json
vendored
22
common/.vscode/launch.json
vendored
@@ -5,17 +5,19 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "pwa-node",
|
||||
"name": "ts-node",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Program",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
"args": [
|
||||
"${workspaceFolder}/src/test.ts",
|
||||
],
|
||||
"program": "${workspaceFolder}/dist/common/src/test.js",
|
||||
"preLaunchTask": "npm: build",
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/**/*.js"
|
||||
]
|
||||
}
|
||||
"runtimeArgs": [
|
||||
"-r",
|
||||
"ts-node/register"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"protocol": "inspector",
|
||||
"internalConsoleOptions": "openOnSessionStart"
|
||||
},
|
||||
]
|
||||
}
|
||||
608
common/package-lock.json
generated
608
common/package-lock.json
generated
@@ -1,22 +1,22 @@
|
||||
{
|
||||
"name": "@scrypted/common",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/common",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.1",
|
||||
"license": "ISC",
|
||||
"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/packages/webrtc": {
|
||||
@@ -74,12 +74,12 @@
|
||||
},
|
||||
"../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.2.68",
|
||||
"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",
|
||||
@@ -111,68 +111,103 @@
|
||||
},
|
||||
"../server": {
|
||||
"name": "@scrypted/server",
|
||||
"version": "0.6.19",
|
||||
"version": "0.82.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
||||
"@mapbox/node-pre-gyp": "^1.0.10",
|
||||
"@scrypted/types": "^0.2.63",
|
||||
"adm-zip": "^0.5.9",
|
||||
"axios": "^0.21.4",
|
||||
"body-parser": "^1.19.0",
|
||||
"@mapbox/node-pre-gyp": "^1.0.11",
|
||||
"@scrypted/types": "^0.3.4",
|
||||
"adm-zip": "^0.5.10",
|
||||
"body-parser": "^1.20.2",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"debug": "^4.3.4",
|
||||
"engine.io": "^6.2.0",
|
||||
"engine.io": "^6.5.4",
|
||||
"express": "^4.18.2",
|
||||
"ffmpeg-static": "^5.1.0",
|
||||
"ffmpeg-static": "^5.2.0",
|
||||
"follow-redirects": "^1.15.4",
|
||||
"http-auth": "^4.2.0",
|
||||
"ip": "^1.1.8",
|
||||
"level": "^6.0.1",
|
||||
"level": "^8.0.0",
|
||||
"linkfs": "^2.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"memfs": "^3.4.7",
|
||||
"memfs": "^4.6.0",
|
||||
"mime": "^3.0.0",
|
||||
"mkdirp": "^1.0.4",
|
||||
"nan": "^2.17.0",
|
||||
"nan": "^2.18.0",
|
||||
"node-dijkstra": "^2.5.0",
|
||||
"node-forge": "^1.3.1",
|
||||
"node-gyp": "^8.4.1",
|
||||
"router": "^1.3.7",
|
||||
"semver": "^7.3.8",
|
||||
"node-gyp": "^10.0.1",
|
||||
"router": "^1.3.8",
|
||||
"semver": "^7.5.4",
|
||||
"sharp": "^0.33.1",
|
||||
"source-map-support": "^0.5.21",
|
||||
"tar": "^6.1.11",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "^4.8.4",
|
||||
"whatwg-mimetype": "^2.3.0",
|
||||
"ws": "^8.9.0"
|
||||
"tar": "^6.2.0",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.3.3",
|
||||
"whatwg-mimetype": "^4.0.0",
|
||||
"ws": "^8.16.0"
|
||||
},
|
||||
"bin": {
|
||||
"scrypted-serve": "bin/scrypted-serve"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/adm-zip": "^0.4.34",
|
||||
"@types/cookie-parser": "^1.4.3",
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/http-auth": "^4.1.1",
|
||||
"@types/ip": "^1.1.0",
|
||||
"@types/lodash": "^4.14.186",
|
||||
"@types/mime": "^3.0.1",
|
||||
"@types/mkdirp": "^1.0.2",
|
||||
"@types/node-dijkstra": "^2.5.3",
|
||||
"@types/node-forge": "^1.3.0",
|
||||
"@types/pem": "^1.9.6",
|
||||
"@types/rimraf": "^3.0.2",
|
||||
"@types/semver": "^7.3.12",
|
||||
"@types/source-map-support": "^0.5.6",
|
||||
"@types/tar": "^4.0.5",
|
||||
"@types/whatwg-mimetype": "^2.1.1",
|
||||
"@types/ws": "^7.4.7"
|
||||
"@types/adm-zip": "^0.5.5",
|
||||
"@types/cookie-parser": "^1.4.6",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/follow-redirects": "^1.14.4",
|
||||
"@types/http-auth": "^4.1.4",
|
||||
"@types/ip": "^1.1.3",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/mime": "^3.0.4",
|
||||
"@types/node-dijkstra": "^2.5.6",
|
||||
"@types/node-forge": "^1.3.10",
|
||||
"@types/pem": "^1.14.4",
|
||||
"@types/semver": "^7.5.6",
|
||||
"@types/source-map-support": "^0.5.10",
|
||||
"@types/tar": "^6.1.10",
|
||||
"@types/whatwg-mimetype": "^3.0.2",
|
||||
"@types/ws": "^8.5.10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"node-pty-prebuilt-multiarch": "^0.10.1-pre.5"
|
||||
}
|
||||
},
|
||||
"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/@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/@scrypted/sdk": {
|
||||
"resolved": "../sdk",
|
||||
"link": true
|
||||
@@ -181,120 +216,215 @@
|
||||
"resolved": "../server",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "16.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.0.tgz",
|
||||
"integrity": "sha512-nmP+VR4oT0pJUPFbKE4SXj3Yb4Q/kz3M9dSAO1GGMebRKWHQxLfDNmU/yh3xxCJha3N60nQ/JwXWwOE/ZSEVag==",
|
||||
"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/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"
|
||||
}
|
||||
],
|
||||
"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/node": {
|
||||
"version": "20.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.0.tgz",
|
||||
"integrity": "sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"node-domexception": "^1.0.0",
|
||||
"web-streams-polyfill": "^3.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20 || >= 14.13"
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
"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": ">=12.20.0"
|
||||
"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/arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true
|
||||
},
|
||||
"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/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/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/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"
|
||||
"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/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
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
"@swc/wasm": {
|
||||
"optional": true
|
||||
}
|
||||
],
|
||||
"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/typescript": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz",
|
||||
"integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==",
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
||||
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"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/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/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/yerror": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/yerror/-/yerror-6.0.1.tgz",
|
||||
"integrity": "sha512-0Bxo+NyeucjxhmPB5z3lmI/N/cOu8L1Q8JVta6/I5G6J/JhCSSPwk8qt9N4yOFSjwkvhDwzUSQglfBIAllvi1Q==",
|
||||
"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": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@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,
|
||||
"requires": {
|
||||
"@jridgewell/trace-mapping": "0.3.9"
|
||||
}
|
||||
},
|
||||
"@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
|
||||
},
|
||||
"@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
|
||||
},
|
||||
"@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,
|
||||
"requires": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"@scrypted/sdk": {
|
||||
"version": "file:../sdk",
|
||||
"requires": {
|
||||
@@ -302,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",
|
||||
@@ -322,117 +452,181 @@
|
||||
"@scrypted/server": {
|
||||
"version": "file:../server",
|
||||
"requires": {
|
||||
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
||||
"@mapbox/node-pre-gyp": "^1.0.10",
|
||||
"@scrypted/types": "^0.2.63",
|
||||
"@types/adm-zip": "^0.4.34",
|
||||
"@types/cookie-parser": "^1.4.3",
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/http-auth": "^4.1.1",
|
||||
"@types/ip": "^1.1.0",
|
||||
"@types/lodash": "^4.14.186",
|
||||
"@types/mime": "^3.0.1",
|
||||
"@types/mkdirp": "^1.0.2",
|
||||
"@types/node-dijkstra": "^2.5.3",
|
||||
"@types/node-forge": "^1.3.0",
|
||||
"@types/pem": "^1.9.6",
|
||||
"@types/rimraf": "^3.0.2",
|
||||
"@types/semver": "^7.3.12",
|
||||
"@types/source-map-support": "^0.5.6",
|
||||
"@types/tar": "^4.0.5",
|
||||
"@types/whatwg-mimetype": "^2.1.1",
|
||||
"@types/ws": "^7.4.7",
|
||||
"adm-zip": "^0.5.9",
|
||||
"axios": "^0.21.4",
|
||||
"body-parser": "^1.19.0",
|
||||
"@mapbox/node-pre-gyp": "^1.0.11",
|
||||
"@scrypted/types": "^0.3.4",
|
||||
"@types/adm-zip": "^0.5.5",
|
||||
"@types/cookie-parser": "^1.4.6",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/follow-redirects": "^1.14.4",
|
||||
"@types/http-auth": "^4.1.4",
|
||||
"@types/ip": "^1.1.3",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/mime": "^3.0.4",
|
||||
"@types/node-dijkstra": "^2.5.6",
|
||||
"@types/node-forge": "^1.3.10",
|
||||
"@types/pem": "^1.14.4",
|
||||
"@types/semver": "^7.5.6",
|
||||
"@types/source-map-support": "^0.5.10",
|
||||
"@types/tar": "^6.1.10",
|
||||
"@types/whatwg-mimetype": "^3.0.2",
|
||||
"@types/ws": "^8.5.10",
|
||||
"adm-zip": "^0.5.10",
|
||||
"body-parser": "^1.20.2",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"debug": "^4.3.4",
|
||||
"engine.io": "^6.2.0",
|
||||
"engine.io": "^6.5.4",
|
||||
"express": "^4.18.2",
|
||||
"ffmpeg-static": "^5.1.0",
|
||||
"ffmpeg-static": "^5.2.0",
|
||||
"follow-redirects": "^1.15.4",
|
||||
"http-auth": "^4.2.0",
|
||||
"ip": "^1.1.8",
|
||||
"level": "^6.0.1",
|
||||
"level": "^8.0.0",
|
||||
"linkfs": "^2.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"memfs": "^3.4.7",
|
||||
"memfs": "^4.6.0",
|
||||
"mime": "^3.0.0",
|
||||
"mkdirp": "^1.0.4",
|
||||
"nan": "^2.17.0",
|
||||
"nan": "^2.18.0",
|
||||
"node-dijkstra": "^2.5.0",
|
||||
"node-forge": "^1.3.1",
|
||||
"node-gyp": "^8.4.1",
|
||||
"node-gyp": "^10.0.1",
|
||||
"node-pty-prebuilt-multiarch": "^0.10.1-pre.5",
|
||||
"router": "^1.3.7",
|
||||
"semver": "^7.3.8",
|
||||
"router": "^1.3.8",
|
||||
"semver": "^7.5.4",
|
||||
"sharp": "^0.33.1",
|
||||
"source-map-support": "^0.5.21",
|
||||
"tar": "^6.1.11",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "^4.8.4",
|
||||
"whatwg-mimetype": "^2.3.0",
|
||||
"ws": "^8.9.0"
|
||||
"tar": "^6.2.0",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.3.3",
|
||||
"whatwg-mimetype": "^4.0.0",
|
||||
"ws": "^8.16.0"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "16.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.0.tgz",
|
||||
"integrity": "sha512-nmP+VR4oT0pJUPFbKE4SXj3Yb4Q/kz3M9dSAO1GGMebRKWHQxLfDNmU/yh3xxCJha3N60nQ/JwXWwOE/ZSEVag==",
|
||||
"@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
|
||||
"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==",
|
||||
"@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
|
||||
},
|
||||
"@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
|
||||
},
|
||||
"@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
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "20.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.0.tgz",
|
||||
"integrity": "sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"node-domexception": "^1.0.0",
|
||||
"web-streams-polyfill": "^3.0.3"
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
"acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"dev": true
|
||||
},
|
||||
"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
|
||||
},
|
||||
"arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true
|
||||
},
|
||||
"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
|
||||
},
|
||||
"diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"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=="
|
||||
"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-fetch-commonjs": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch-commonjs/-/node-fetch-commonjs-3.1.1.tgz",
|
||||
"integrity": "sha512-TgkdVJdiEaauzWwB9NoD4TvHZFtG6KKEffvotWf9WNIyoRZHsCFjGfb3bhkIXrMt3YFgFi8ZApbwWoe1h3XTpA==",
|
||||
"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,
|
||||
"requires": {
|
||||
"formdata-polyfill": "^4.0.10",
|
||||
"web-streams-polyfill": "^3.1.1"
|
||||
"@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"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz",
|
||||
"integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA=="
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
||||
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw=="
|
||||
},
|
||||
"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=="
|
||||
"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
|
||||
},
|
||||
"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
|
||||
},
|
||||
"yerror": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/yerror/-/yerror-6.0.1.tgz",
|
||||
"integrity": "sha512-0Bxo+NyeucjxhmPB5z3lmI/N/cOu8L1Q8JVta6/I5G6J/JhCSSPwk8qt9N4yOFSjwkvhDwzUSQglfBIAllvi1Q=="
|
||||
"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",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/server": "file:../server",
|
||||
"@scrypted/sdk": "file:../sdk",
|
||||
"http-auth-utils": "^3.0.2",
|
||||
"node-fetch-commonjs": "^3.1.1",
|
||||
"typescript": "^4.4.3"
|
||||
"@scrypted/server": "file:../server",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import sdk from "@scrypted/sdk";
|
||||
|
||||
const { systemManager, log } = sdk;
|
||||
|
||||
export async function alertRecommendedPlugins(plugins: { [pkg: string]: string }) {
|
||||
const pluginsComponent = await systemManager.getComponent('plugins');
|
||||
let recommended: any;
|
||||
try {
|
||||
recommended = JSON.parse(localStorage.getItem('alert-recommended'));
|
||||
}
|
||||
catch (e) {
|
||||
recommended = {};
|
||||
}
|
||||
for (const plugin of Object.keys(plugins)) {
|
||||
try {
|
||||
if (recommended[plugin])
|
||||
continue;
|
||||
|
||||
recommended[plugin] = true;
|
||||
localStorage.setItem('alert-recommended', JSON.stringify(recommended));
|
||||
const id = await pluginsComponent.getIdForPluginId(plugin);
|
||||
if (id)
|
||||
continue;
|
||||
const name = plugins[plugin];
|
||||
log.a(`Installation of the ${name} plugin is also recommended. origin:/#/component/plugin/install/${plugin}`)
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ class EndError extends Error {
|
||||
|
||||
export function createAsyncQueue<T>() {
|
||||
let ended: Error | undefined;
|
||||
const endDeferred = new Deferred<void>();
|
||||
const waiting: Deferred<T>[] = [];
|
||||
const queued: { item: T, dequeued?: Deferred<void> }[] = [];
|
||||
|
||||
@@ -23,6 +24,17 @@ export function createAsyncQueue<T>() {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
const take = () => {
|
||||
if (queued.length) {
|
||||
const { item, dequeued: enqueue } = queued.shift()!;
|
||||
enqueue?.resolve();
|
||||
return item;
|
||||
}
|
||||
|
||||
if (ended)
|
||||
throw ended;
|
||||
}
|
||||
|
||||
const submit = (item: T, dequeued?: Deferred<void>, signal?: AbortSignal) => {
|
||||
if (ended)
|
||||
return false;
|
||||
@@ -34,36 +46,65 @@ export function createAsyncQueue<T>() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (signal)
|
||||
dequeued ||= new Deferred();
|
||||
|
||||
const qi = {
|
||||
item,
|
||||
dequeued,
|
||||
};
|
||||
queued!.push(qi);
|
||||
|
||||
signal?.addEventListener('abort', () => {
|
||||
if (!signal)
|
||||
return true;
|
||||
|
||||
const h = () => {
|
||||
const index = queued.indexOf(qi);
|
||||
if (index === -1)
|
||||
return;
|
||||
queued.splice(index, 1);
|
||||
dequeued?.reject(new Error('abort'));
|
||||
});
|
||||
};
|
||||
|
||||
dequeued.promise.catch(() => {}).finally(() => signal.removeEventListener('abort', h));
|
||||
signal.addEventListener('abort', h);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function end(e?: Error) {
|
||||
if (ended)
|
||||
return false;
|
||||
// catch to prevent unhandled rejection.
|
||||
ended = e || new EndError();
|
||||
endDeferred.resolve();
|
||||
while (waiting.length) {
|
||||
waiting.shift().reject(ended);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function queue() {
|
||||
return (async function* () {
|
||||
while (true) {
|
||||
try {
|
||||
const item = await dequeue();
|
||||
yield item;
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof EndError)
|
||||
return;
|
||||
throw e;
|
||||
try {
|
||||
while (true) {
|
||||
try {
|
||||
const item = await dequeue();
|
||||
yield item;
|
||||
}
|
||||
catch (e) {
|
||||
// the yield above may raise an error, and the queue should be ended.
|
||||
end(e);
|
||||
if (e instanceof EndError)
|
||||
return;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
// the yield above may cause an iterator return, and the queue should be ended.
|
||||
end();
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
@@ -82,6 +123,11 @@ export function createAsyncQueue<T>() {
|
||||
}
|
||||
|
||||
return {
|
||||
get ended() {
|
||||
return ended;
|
||||
},
|
||||
endPromise: endDeferred.promise,
|
||||
take,
|
||||
clear() {
|
||||
return clear();
|
||||
},
|
||||
@@ -94,14 +140,7 @@ export function createAsyncQueue<T>() {
|
||||
submit(item: T, signal?: AbortSignal) {
|
||||
return submit(item, undefined, signal);
|
||||
},
|
||||
end(e?: Error) {
|
||||
if (ended)
|
||||
return false;
|
||||
// catch to prevent unhandled rejection.
|
||||
ended = e || new EndError()
|
||||
clear(e);
|
||||
return true;
|
||||
},
|
||||
end,
|
||||
async enqueue(item: T, signal?: AbortSignal) {
|
||||
const dequeued = new Deferred<void>();
|
||||
if (!submit(item, dequeued, signal))
|
||||
|
||||
@@ -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,
|
||||
@@ -58,18 +59,12 @@ export async function scryptedEval(device: ScryptedDeviceBase, script: string, e
|
||||
worker.worker.terminate();
|
||||
}
|
||||
|
||||
const smProxy = new SystemManagerImpl();
|
||||
smProxy.state = systemManager.getSystemState();
|
||||
const apiProxy = new PluginAPIProxy(sdk.pluginHostAPI);
|
||||
smProxy.api = apiProxy;
|
||||
|
||||
const allParams = Object.assign({}, params, {
|
||||
sdk,
|
||||
fs: require('realfs'),
|
||||
fetch,
|
||||
ScryptedDeviceBase,
|
||||
MixinDeviceBase,
|
||||
systemManager: smProxy,
|
||||
systemManager,
|
||||
deviceManager,
|
||||
endpointManager,
|
||||
mediaManager,
|
||||
@@ -104,7 +99,6 @@ export async function scryptedEval(device: ScryptedDeviceBase, script: string, e
|
||||
return {
|
||||
value,
|
||||
defaultExport,
|
||||
apiProxy,
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
@@ -115,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,
|
||||
|
||||
50
common/src/http-auth-fetch.ts
Normal file
50
common/src/http-auth-fetch.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
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 type { HttpFetchOptions, HttpFetchResponseType } from '../../server/src/fetch/http-fetch';
|
||||
export type { AuthFetchCredentialState, AuthFetchOptions } from '../../packages/auth-fetch/src/auth-fetch';
|
||||
|
||||
export const authHttpFetch = createAuthFetch<Readable, IncomingMessage>(httpFetch, httpFetchParseIncomingMessage);
|
||||
|
||||
function ensureType<T>(v: T) {
|
||||
}
|
||||
|
||||
async function test() {
|
||||
const a = await authHttpFetch({
|
||||
credential: undefined,
|
||||
url: 'http://example.com',
|
||||
});
|
||||
|
||||
ensureType<Buffer>(a.body);
|
||||
|
||||
const b = await authHttpFetch({
|
||||
credential: undefined,
|
||||
url: 'http://example.com',
|
||||
responseType: 'json',
|
||||
});
|
||||
ensureType<any>(b.body);
|
||||
|
||||
const c = await authHttpFetch({
|
||||
credential: undefined,
|
||||
url: 'http://example.com',
|
||||
responseType: 'readable',
|
||||
});
|
||||
ensureType<IncomingMessage>(c.body);
|
||||
|
||||
const d = await authHttpFetch({
|
||||
credential: undefined,
|
||||
url: 'http://example.com',
|
||||
responseType: 'buffer',
|
||||
});
|
||||
ensureType<Buffer>(d.body);
|
||||
|
||||
const e = await authHttpFetch({
|
||||
credential: undefined,
|
||||
url: 'http://example.com',
|
||||
responseType: 'text',
|
||||
});
|
||||
ensureType<string>(e.body);
|
||||
}
|
||||
|
||||
@@ -78,11 +78,17 @@ export function createPromiseDebouncer<T>() {
|
||||
export function createMapPromiseDebouncer<T>() {
|
||||
const map = new Map<string, Promise<T>>();
|
||||
|
||||
return (key: any, func: () => Promise<T>): Promise<T> => {
|
||||
return (key: any, debounce: number, func: () => Promise<T>): Promise<T> => {
|
||||
const keyStr = JSON.stringify(key);
|
||||
let value = map.get(keyStr);
|
||||
if (!value) {
|
||||
value = func().finally(() => map.delete(keyStr));
|
||||
value = func().finally(() => {
|
||||
if (!debounce) {
|
||||
map.delete(keyStr);
|
||||
return;
|
||||
}
|
||||
setTimeout(() => map.delete(keyStr), debounce);
|
||||
});
|
||||
map.set(keyStr, value);
|
||||
}
|
||||
return value;
|
||||
|
||||
@@ -19,7 +19,7 @@ export async function read16BELengthLoop(readable: Readable, options: {
|
||||
let length: number;
|
||||
let skipCount = 0;
|
||||
let readCount = 0;
|
||||
|
||||
|
||||
const resumeRead = () => {
|
||||
readCount++;
|
||||
read();
|
||||
@@ -59,13 +59,13 @@ export async function read16BELengthLoop(readable: Readable, options: {
|
||||
|
||||
export class StreamEndError extends Error {
|
||||
constructor() {
|
||||
super()
|
||||
super('stream ended');
|
||||
}
|
||||
}
|
||||
|
||||
export async function readLength(readable: Readable, length: number): Promise<Buffer> {
|
||||
if (readable.readableEnded || readable.destroyed)
|
||||
throw new Error("stream ended");
|
||||
throw new StreamEndError();
|
||||
|
||||
if (!length) {
|
||||
return Buffer.alloc(0);
|
||||
@@ -109,17 +109,26 @@ export async function readLength(readable: Readable, length: number): Promise<Bu
|
||||
const CHARCODE_NEWLINE = '\n'.charCodeAt(0);
|
||||
|
||||
export async function readUntil(readable: Readable, charCode: number) {
|
||||
const data = [];
|
||||
let count = 0;
|
||||
const queued: Buffer[] = [];
|
||||
while (true) {
|
||||
const buffer = await readLength(readable, 1);
|
||||
if (!buffer)
|
||||
throw new Error("end of stream");
|
||||
if (buffer[0] === charCode)
|
||||
break;
|
||||
data[count++] = buffer[0];
|
||||
const available: Buffer = readable.read();
|
||||
if (!available) {
|
||||
await once(readable, 'readable');
|
||||
continue;
|
||||
}
|
||||
const index = available.findIndex(b => b === charCode);
|
||||
if (index === -1) {
|
||||
queued.push(available);
|
||||
continue;
|
||||
}
|
||||
|
||||
const before = available.subarray(0, index);
|
||||
queued.push(before);
|
||||
|
||||
const after = available.subarray(index + 1);
|
||||
readable.unshift(after);
|
||||
return Buffer.concat(queued).toString();
|
||||
}
|
||||
return Buffer.from(data).toString();
|
||||
}
|
||||
|
||||
export async function readLine(readable: Readable) {
|
||||
|
||||
@@ -73,6 +73,14 @@ function createOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
// can be called on anything with getStats, ie for receiver specific reports or connection reports.
|
||||
export async function getPacketsLost(t: { getStats(): Promise<RTCStatsReport> }) {
|
||||
const stats = await t.getStats();
|
||||
const packetsLost = ([...stats.values()] as { packetsLost: number }[]).filter(stat => 'packetsLost' in stat).map(stat => stat.packetsLost);
|
||||
const total = packetsLost.reduce((p, c) => p + c, 0);
|
||||
return total;
|
||||
}
|
||||
|
||||
export class BrowserSignalingSession implements RTCSignalingSession {
|
||||
private pc: RTCPeerConnection;
|
||||
pcDeferred = new Deferred<RTCPeerConnection>();
|
||||
@@ -90,6 +98,10 @@ export class BrowserSignalingSession implements RTCSignalingSession {
|
||||
return this.options;
|
||||
}
|
||||
|
||||
async getPacketsLost() {
|
||||
return getPacketsLost(this.pc);
|
||||
}
|
||||
|
||||
async setMicrophone(enabled: boolean) {
|
||||
if (this.microphone && enabled && !this.micEnabled) {
|
||||
this.micEnabled = true;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -149,7 +149,7 @@ export function parseFmtp(msection: string[]) {
|
||||
const paramLine = fmtpLine.substring(firstSpace + 1);
|
||||
const payloadType = parseInt(fmtp.split(':')[1]);
|
||||
|
||||
if (!fmtp || !paramLine || Number.isNaN( payloadType )) {
|
||||
if (!fmtp || !paramLine || Number.isNaN(payloadType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -170,28 +170,47 @@ export function parseFmtp(msection: string[]) {
|
||||
}
|
||||
|
||||
export type MSection = ReturnType<typeof parseMSection>;
|
||||
export type RTPMap = ReturnType<typeof parseRtpMap>;
|
||||
|
||||
export function parseRtpMap(mlineType: string, rtpmap: string) {
|
||||
const match = rtpmap?.match(/a=rtpmap:([\d]+) (.*?)\/([\d]+)/);
|
||||
const match = rtpmap?.match(/a=rtpmap:([\d]+) (.*?)\/([\d]+)(\/([\d]+))?/);
|
||||
|
||||
rtpmap = rtpmap?.toLowerCase();
|
||||
|
||||
let codec: string;
|
||||
let ffmpegEncoder: string;
|
||||
if (rtpmap?.includes('mpeg4')) {
|
||||
codec = 'aac';
|
||||
ffmpegEncoder = 'aac';
|
||||
}
|
||||
else if (rtpmap?.includes('opus')) {
|
||||
codec = 'opus';
|
||||
ffmpegEncoder = 'libopus';
|
||||
}
|
||||
else if (rtpmap?.includes('pcma')) {
|
||||
codec = 'pcm_alaw';
|
||||
ffmpegEncoder = 'pcm_alaw';
|
||||
}
|
||||
else if (rtpmap?.includes('pcmu')) {
|
||||
codec = 'pcm_ulaw';
|
||||
codec = 'pcm_mulaw';
|
||||
ffmpegEncoder = 'pcm_mulaw';
|
||||
}
|
||||
else if (rtpmap?.includes('g726')) {
|
||||
codec = 'g726';
|
||||
// disabled since it 48000 is non compliant in ffmpeg and fails.
|
||||
// ffmpegEncoder = 'g726';
|
||||
}
|
||||
else if (rtpmap?.includes('pcm')) {
|
||||
codec = 'pcm';
|
||||
}
|
||||
else if (rtpmap?.includes('l16')) {
|
||||
codec = 'pcm_s16be';
|
||||
ffmpegEncoder = 'pcm_s16be';
|
||||
}
|
||||
else if (rtpmap?.includes('speex')) {
|
||||
codec = 'speex';
|
||||
ffmpegEncoder = 'libspeex';
|
||||
}
|
||||
else if (rtpmap?.includes('h264')) {
|
||||
codec = 'h264';
|
||||
}
|
||||
@@ -207,8 +226,10 @@ export function parseRtpMap(mlineType: string, rtpmap: string) {
|
||||
return {
|
||||
line: rtpmap,
|
||||
codec,
|
||||
ffmpegEncoder,
|
||||
rawCodec: match?.[2],
|
||||
clock: parseInt(match?.[3]),
|
||||
channels: parseInt(match?.[5]) || undefined,
|
||||
payloadType: parseInt(match?.[1]),
|
||||
}
|
||||
}
|
||||
@@ -220,9 +241,11 @@ export function parseMSection(msection: string[]) {
|
||||
const mline = parseMLine(msection[0]);
|
||||
const rawRtpmaps = msection.filter(line => line.startsWith(artpmap));
|
||||
const rtpmaps = rawRtpmaps.map(line => parseRtpMap(mline.type, line));
|
||||
const codec = parseRtpMap(mline.type, rawRtpmaps[0]).codec;
|
||||
// if no rtp map is specified, pcm_alaw is used. parsing a null rtpmap is valid.
|
||||
const rtpmap = parseRtpMap(mline.type, rawRtpmaps[0]);
|
||||
const { codec } = rtpmap;
|
||||
let direction: string;
|
||||
|
||||
|
||||
for (const checkDirection of ['sendonly', 'sendrecv', 'recvonly', 'inactive']) {
|
||||
const found = msection.find(line => line === 'a=' + checkDirection);
|
||||
if (found) {
|
||||
@@ -239,6 +262,7 @@ export function parseMSection(msection: string[]) {
|
||||
contents: msection.join('\r\n'),
|
||||
control,
|
||||
codec,
|
||||
rtpmap,
|
||||
direction,
|
||||
toSdp: () => {
|
||||
return ret.lines.join('\r\n');
|
||||
|
||||
77
common/src/zygote.ts
Normal file
77
common/src/zygote.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import sdk, { PluginFork } from '@scrypted/sdk';
|
||||
import worker_threads from 'worker_threads';
|
||||
import { createAsyncQueue } from './async-queue';
|
||||
import os from 'os';
|
||||
|
||||
export type Zygote<T> = () => PluginFork<T>;
|
||||
|
||||
export function createZygote<T>(): Zygote<T> {
|
||||
if (!worker_threads.isMainThread)
|
||||
return;
|
||||
|
||||
let zygote = sdk.fork<T>();
|
||||
function* next() {
|
||||
while (true) {
|
||||
const cur = zygote;
|
||||
zygote = sdk.fork<T>();
|
||||
yield cur;
|
||||
}
|
||||
}
|
||||
|
||||
const gen = next();
|
||||
return () => gen.next().value as PluginFork<T>;
|
||||
}
|
||||
|
||||
|
||||
export function createZygoteWorkQueue<T>(maxWorkers: number = os.cpus().length >> 1) {
|
||||
const queue = createAsyncQueue<(doWork: (fork: PluginFork<T>) => Promise<any>) => Promise<any>>();
|
||||
let forks = 0;
|
||||
|
||||
return async <R>(doWork: (fork: PluginFork<T>) => Promise<R>): Promise<R> => {
|
||||
const check = queue.take();
|
||||
if (check)
|
||||
return check(doWork);
|
||||
|
||||
if (maxWorkers && forks < maxWorkers) {
|
||||
let exited = false;
|
||||
const controller = new AbortController();
|
||||
// necessary to prevent unhandledrejection errors
|
||||
controller.signal.addEventListener('abort', () => { });
|
||||
const fork = sdk.fork<T>();
|
||||
forks++;
|
||||
fork.worker.on('exit', () => {
|
||||
forks--;
|
||||
exited = true;
|
||||
controller.abort();
|
||||
});
|
||||
|
||||
let timeout: NodeJS.Timeout;
|
||||
const queueFork = () => {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => {
|
||||
// keep one alive.
|
||||
if (forks === 1)
|
||||
return;
|
||||
fork.worker.terminate();
|
||||
}, 30000);
|
||||
|
||||
queue.submit(async v2 => {
|
||||
clearTimeout(timeout);
|
||||
try {
|
||||
return await v2(fork);
|
||||
}
|
||||
finally {
|
||||
if (!exited) {
|
||||
queueFork();
|
||||
}
|
||||
}
|
||||
}, controller.signal);
|
||||
}
|
||||
|
||||
queueFork();
|
||||
}
|
||||
|
||||
const d = await queue.dequeue();
|
||||
return d(doWork);
|
||||
};
|
||||
}
|
||||
1
external/axios-digest-auth
vendored
1
external/axios-digest-auth
vendored
Submodule external/axios-digest-auth deleted from d0872934e6
2
external/ring-client-api
vendored
2
external/ring-client-api
vendored
Submodule external/ring-client-api updated: 4e95093f76...3797e311ed
2
external/unifi-protect
vendored
2
external/unifi-protect
vendored
Submodule external/unifi-protect updated: 3759ba334f...bf6fdbdc65
2
external/werift
vendored
2
external/werift
vendored
Submodule external/werift updated: b63f339b55...a0070297a4
@@ -1,6 +1,6 @@
|
||||
# Home Assistant Addon Configuration
|
||||
name: Scrypted
|
||||
version: "18-jammy-full.s6-v0.55.0"
|
||||
version: "18-jammy-full.s6-v0.85.0"
|
||||
slug: scrypted
|
||||
description: Scrypted is a high performance home video integration and automation platform
|
||||
url: "https://github.com/koush/scrypted"
|
||||
@@ -29,9 +29,9 @@ environment:
|
||||
SCRYPTED_ADMIN_USERNAME: "homeassistant"
|
||||
SCRYPTED_INSTALL_ENVIRONMENT: "ha"
|
||||
backup_exclude:
|
||||
- '/server/**'
|
||||
- '/data/scrypted_nvr/**'
|
||||
- '/data/scrypted_data/plugins/**'
|
||||
- '*/server/**'
|
||||
- '*/scrypted_nvr/**'
|
||||
- '*/scrypted_data/plugins/**'
|
||||
map:
|
||||
- config:rw
|
||||
- media:rw
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
ARG BASE="18-jammy-full"
|
||||
FROM koush/scrypted-common:${BASE}
|
||||
FROM ghcr.io/koush/scrypted-common:${BASE}
|
||||
|
||||
WORKDIR /
|
||||
# cache bust
|
||||
@@ -14,4 +14,8 @@ WORKDIR /server
|
||||
# https://github.com/nodejs/node/issues/41145#issuecomment-992948130
|
||||
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="20241303"
|
||||
|
||||
CMD npm --prefix /server exec scrypted-serve
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
ARG BASE="16-jammy"
|
||||
FROM koush/scrypted-common:${BASE}
|
||||
FROM ghcr.io/koush/scrypted-common:${BASE}
|
||||
|
||||
WORKDIR /
|
||||
RUN git clone --depth=1 https://github.com/koush/scrypted
|
||||
|
||||
@@ -25,10 +25,14 @@ RUN apt-get update && apt-get -y install \
|
||||
apt-get -y upgrade
|
||||
|
||||
ARG NODE_VERSION=18
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash -
|
||||
RUN apt-get install -y ca-certificates curl gnupg
|
||||
RUN mkdir -p /etc/apt/keyrings
|
||||
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor --yes -o /etc/apt/keyrings/nodesource.gpg
|
||||
RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_"$NODE_VERSION".x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
|
||||
RUN apt-get update && apt-get install -y nodejs
|
||||
|
||||
# python native
|
||||
RUN echo "Installing python."
|
||||
RUN apt-get -y install \
|
||||
python3 \
|
||||
python3-dev \
|
||||
@@ -38,44 +42,25 @@ RUN apt-get -y install \
|
||||
|
||||
# these are necessary for pillow-simd, additional on disk size is small
|
||||
# but could consider removing this.
|
||||
RUN echo "Installing pillow-simd dependencies."
|
||||
RUN apt-get -y install \
|
||||
libjpeg-dev zlib1g-dev
|
||||
|
||||
# plugins support fallback to pillow, but vips is faster.
|
||||
RUN apt-get -y install \
|
||||
libvips
|
||||
|
||||
# gstreamer native https://gstreamer.freedesktop.org/documentation/installing/on-linux.html?gi-language=c#install-gstreamer-on-ubuntu-or-debian
|
||||
RUN echo "Installing gstreamer."
|
||||
RUN apt-get -y install \
|
||||
gstreamer1.0-tools gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav gstreamer1.0-alsa \
|
||||
gstreamer1.0-vaapi
|
||||
|
||||
# python3 gstreamer bindings
|
||||
RUN echo "Installing gstreamer bindings."
|
||||
RUN apt-get -y install \
|
||||
python3-gst-1.0
|
||||
|
||||
# armv7l does not have wheels for any of these
|
||||
# and compile times would forever, if it works at all.
|
||||
# furthermore, it's possible to run 32bit docker on 64bit arm,
|
||||
# which causes weird behavior in python which looks at the arch version
|
||||
# which still reports 64bit, even if running in 32bit docker.
|
||||
# this scenario is not supported and will be reported at runtime.
|
||||
# this bit is not necessary on amd64, but leaving it for consistency.
|
||||
RUN apt-get -y install \
|
||||
python3-matplotlib \
|
||||
python3-numpy \
|
||||
python3-opencv \
|
||||
python3-pil \
|
||||
python3-skimage
|
||||
|
||||
# allow pip to install to system
|
||||
RUN rm -f /usr/lib/python**/EXTERNALLY-MANAGED
|
||||
|
||||
# pyvips is broken on x86 due to mismatch ffi
|
||||
# https://stackoverflow.com/questions/62658237/it-seems-that-the-version-of-the-libffi-library-seen-at-runtime-is-different-fro
|
||||
|
||||
RUN python3 -m pip install --upgrade pip
|
||||
RUN python3 -m pip install --force-reinstall --no-binary :all: cffi
|
||||
RUN python3 -m pip install debugpy typing_extensions psutil
|
||||
|
||||
################################################################
|
||||
@@ -87,20 +72,11 @@ RUN python3 -m pip install debugpy typing_extensions psutil
|
||||
FROM header as base
|
||||
|
||||
# intel opencl gpu for openvino
|
||||
RUN bash -c "if [ \"$(uname -m)\" == \"x86_64\" ]; \
|
||||
then \
|
||||
apt-get update && apt-get install -y gpg-agent && \
|
||||
rm -f /usr/share/keyrings/intel-graphics.gpg && \
|
||||
curl -L https://repositories.intel.com/graphics/intel-graphics.key | gpg --dearmor --output /usr/share/keyrings/intel-graphics.gpg && \
|
||||
echo 'deb [arch=amd64,i386 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/graphics/ubuntu jammy arc' | tee /etc/apt/sources.list.d/intel.gpu.jammy.list && \
|
||||
apt-get -y update && \
|
||||
apt-get -y install intel-opencl-icd intel-media-va-driver-non-free && \
|
||||
apt-get -y dist-upgrade; \
|
||||
fi"
|
||||
RUN curl https://raw.githubusercontent.com/koush/scrypted/main/install/docker/install-intel-graphics.sh | bash
|
||||
|
||||
# python 3.9 from ppa.
|
||||
# 3.9 is the version with prebuilt support for tensorflow lite
|
||||
RUN add-apt-repository ppa:deadsnakes/ppa && \
|
||||
RUN add-apt-repository -y ppa:deadsnakes/ppa && \
|
||||
apt-get -y install \
|
||||
python3.9 \
|
||||
python3.9-dev \
|
||||
@@ -110,7 +86,6 @@ RUN add-apt-repository ppa:deadsnakes/ppa && \
|
||||
RUN rm -f /usr/lib/python**/EXTERNALLY-MANAGED
|
||||
|
||||
RUN python3.9 -m pip install --upgrade pip
|
||||
RUN python3.9 -m pip install --force-reinstall --no-binary :all: cffi
|
||||
RUN python3.9 -m pip install debugpy typing_extensions psutil
|
||||
|
||||
# Coral Edge TPU
|
||||
@@ -127,9 +102,6 @@ ENV SCRYPTED_INSTALL_PATH="/server"
|
||||
RUN test -f "/usr/bin/ffmpeg"
|
||||
ENV SCRYPTED_FFMPEG_PATH="/usr/bin/ffmpeg"
|
||||
|
||||
# changing this forces pip and npm to perform reinstalls.
|
||||
# if this base image changes, this version must be updated.
|
||||
ENV SCRYPTED_BASE_VERSION="20230727"
|
||||
ENV SCRYPTED_DOCKER_FLAVOR="full"
|
||||
|
||||
################################################################
|
||||
|
||||
@@ -6,18 +6,15 @@ 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
|
||||
|
||||
ARG NODE_VERSION=18
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash -
|
||||
RUN apt-get install -y ca-certificates curl gnupg
|
||||
RUN mkdir -p /etc/apt/keyrings
|
||||
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor --yes -o /etc/apt/keyrings/nodesource.gpg
|
||||
RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_"$NODE_VERSION".x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
|
||||
RUN apt-get update && apt-get install -y nodejs
|
||||
|
||||
# python native
|
||||
@@ -41,7 +38,4 @@ ENV SCRYPTED_INSTALL_PATH="/server"
|
||||
RUN test -f "/usr/bin/ffmpeg"
|
||||
ENV SCRYPTED_FFMPEG_PATH="/usr/bin/ffmpeg"
|
||||
|
||||
# changing this forces pip and npm to perform reinstalls.
|
||||
# if this base image changes, this version must be updated.
|
||||
ENV SCRYPTED_BASE_VERSION="20230727"
|
||||
ENV SCRYPTED_DOCKER_FLAVOR="lite"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM koush/scrypted-common
|
||||
FROM ghcr.io/koush/scrypted-common
|
||||
|
||||
WORKDIR /
|
||||
COPY . .
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM koush/18-jammy-full.s6
|
||||
FROM ghcr.io/koush/scrypted:18-jammy-full.s6
|
||||
|
||||
WORKDIR /
|
||||
|
||||
@@ -12,11 +12,3 @@ ENV PATH=$CONDA_DIR/bin:$PATH
|
||||
RUN conda install -c conda-forge cudatoolkit=11.2.2 cudnn=8.1.0
|
||||
ENV CONDA_PREFIX=/opt/conda
|
||||
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$CONDA_PREFIX/lib/
|
||||
|
||||
# this is a copy pasta, seems to need a reinstall.
|
||||
# python pip
|
||||
RUN python3 -m pip install --upgrade pip
|
||||
# pyvips is broken on x86 due to mismatch ffi
|
||||
# https://stackoverflow.com/questions/62658237/it-seems-that-the-version-of-the-libffi-library-seen-at-runtime-is-different-fro
|
||||
RUN python3 -m pip install --force-reinstall --no-binary :all: cffi
|
||||
RUN python3 -m pip install debugpy typing_extensions psutil
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
ARG BASE="18-jammy-full"
|
||||
FROM koush/scrypted-common:${BASE}
|
||||
FROM ghcr.io/koush/scrypted-common:${BASE}
|
||||
|
||||
# avahi advertiser support
|
||||
RUN apt-get update && apt-get -y install \
|
||||
@@ -44,4 +44,8 @@ WORKDIR /server
|
||||
# https://github.com/nodejs/node/issues/41145#issuecomment-992948130
|
||||
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="20241303"
|
||||
|
||||
CMD npm --prefix /server exec scrypted-serve
|
||||
|
||||
@@ -9,7 +9,7 @@ RUN apt-get -y update && \
|
||||
|
||||
# switch to nvm?
|
||||
ARG NODE_VERSION=18
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - && apt-get update && apt-get install -y nodejs
|
||||
RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
|
||||
|
||||
ENV SCRYPTED_INSTALL_ENVIRONMENT="docker"
|
||||
ENV SCRYPTED_CAN_RESTART="true"
|
||||
@@ -19,7 +19,4 @@ ENV SCRYPTED_INSTALL_PATH="/server"
|
||||
RUN test -f "/usr/bin/ffmpeg"
|
||||
ENV SCRYPTED_FFMPEG_PATH="/usr/bin/ffmpeg"
|
||||
|
||||
# changing this forces pip and npm to perform reinstalls.
|
||||
# if this base image changes, this version must be updated.
|
||||
ENV SCRYPTED_BASE_VERSION="20230727"
|
||||
ENV SCRYPTED_DOCKER_FLAVOR="thin"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
./template/generate-dockerfile.sh
|
||||
|
||||
docker build -t koush/scrypted-common -f Dockerfile.common . && \
|
||||
docker build -t koush/scrypted -f Dockerfile.local .
|
||||
docker build -t ghcr.io/koush/scrypted-common -f Dockerfile.full . && \
|
||||
docker build -t ghcr.io/koush/scrypted -f Dockerfile.local .
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
./docker-build.sh
|
||||
|
||||
docker build -t koush/scrypted:18-jammy-full.nvidia -f Dockerfile.nvidia
|
||||
docker build -t ghcr.io/koush/scrypted:18-jammy-full.nvidia -f Dockerfile.nvidia .
|
||||
@@ -11,8 +11,8 @@ echo $BASE
|
||||
SUPERVISOR=.s6
|
||||
SUPERVISOR_BASE=$BASE$SUPERVISOR
|
||||
|
||||
docker build -t koush/scrypted-common:$BASE -f Dockerfile.$FLAVOR \
|
||||
docker build -t ghcr.io/koush/scrypted-common:$BASE -f Dockerfile.$FLAVOR \
|
||||
--build-arg NODE_VERSION=$NODE_VERSION --build-arg BASE=$IMAGE_BASE . && \
|
||||
\
|
||||
docker build -t koush/scrypted:$SUPERVISOR_BASE -f Dockerfile$SUPERVISOR \
|
||||
docker build -t ghcr.io/koush/scrypted:$SUPERVISOR_BASE -f Dockerfile$SUPERVISOR \
|
||||
--build-arg BASE=$BASE --build-arg SCRYPTED_INSTALL_VERSION=$SCRYPTED_INSTALL_VERSION .
|
||||
|
||||
@@ -34,14 +34,14 @@ services:
|
||||
- SCRYPTED_WEBHOOK_UPDATE_AUTHORIZATION=Bearer SET_THIS_TO_SOME_RANDOM_TEXT
|
||||
- SCRYPTED_WEBHOOK_UPDATE=http://localhost:10444/v1/update
|
||||
|
||||
# Uncomment next 3 lines for Nvidia GPU support.
|
||||
# - NVIDIA_VISIBLE_DEVICES=all
|
||||
# - NVIDIA_DRIVER_CAPABILITIES=all
|
||||
|
||||
# Uncomment next line to run avahi-daemon inside the container
|
||||
# Don't use if dbus and avahi run on the host and are bind-mounted
|
||||
# (see below under "volumes")
|
||||
# - SCRYPTED_DOCKER_AVAHI=true
|
||||
|
||||
# Uncomment next 3 lines for Nvidia GPU support.
|
||||
# - NVIDIA_VISIBLE_DEVICES=all
|
||||
# - NVIDIA_DRIVER_CAPABILITIES=all
|
||||
# runtime: nvidia
|
||||
|
||||
volumes:
|
||||
@@ -90,7 +90,7 @@ services:
|
||||
container_name: scrypted
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
image: koush/scrypted
|
||||
image: ghcr.io/koush/scrypted
|
||||
|
||||
# logging is noisy and will unnecessarily wear on flash storage.
|
||||
# scrypted has per device in memory logging that is preferred.
|
||||
|
||||
16
install/docker/install-intel-graphics.sh
Normal file
16
install/docker/install-intel-graphics.sh
Normal file
@@ -0,0 +1,16 @@
|
||||
if [ "$(uname -m)" = "x86_64" ]
|
||||
then
|
||||
echo "Installing Intel graphics packages."
|
||||
apt-get update && apt-get install -y gpg-agent &&
|
||||
rm -f /usr/share/keyrings/intel-graphics.gpg &&
|
||||
curl -L https://repositories.intel.com/graphics/intel-graphics.key | gpg --dearmor --yes --output /usr/share/keyrings/intel-graphics.gpg &&
|
||||
echo 'deb [arch=amd64,i386 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/graphics/ubuntu jammy arc' | tee /etc/apt/sources.list.d/intel.gpu.jammy.list &&
|
||||
apt-get -y update &&
|
||||
apt-get -y install intel-opencl-icd intel-media-va-driver-non-free &&
|
||||
apt-get -y dist-upgrade;
|
||||
exit $?
|
||||
else
|
||||
echo "Intel graphics will not be installed on this architecture."
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -6,19 +6,6 @@ then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$SERVICE_USER" == "root" ]
|
||||
then
|
||||
echo "Scrypted SERVICE_USER root is not allowed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
USER_HOME=$(eval echo ~$SERVICE_USER)
|
||||
SCRYPTED_HOME=$USER_HOME/.scrypted
|
||||
mkdir -p $SCRYPTED_HOME
|
||||
|
||||
set -e
|
||||
cd $SCRYPTED_HOME
|
||||
|
||||
function readyn() {
|
||||
while true; do
|
||||
read -p "$1 (y/n) " yn
|
||||
@@ -30,6 +17,22 @@ function readyn() {
|
||||
done
|
||||
}
|
||||
|
||||
if [ "$SERVICE_USER" == "root" ]
|
||||
then
|
||||
readyn "Scrypted will store its files in the root user home directory. Running as a non-root user is recommended. Are you sure?"
|
||||
if [ "$yn" == "n" ]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
USER_HOME=$(eval echo ~$SERVICE_USER)
|
||||
SCRYPTED_HOME=$USER_HOME/.scrypted
|
||||
mkdir -p $SCRYPTED_HOME
|
||||
|
||||
set -e
|
||||
cd $SCRYPTED_HOME
|
||||
|
||||
readyn "Install Docker?"
|
||||
|
||||
if [ "$yn" == "y" ]
|
||||
|
||||
@@ -4,20 +4,11 @@
|
||||
FROM header as base
|
||||
|
||||
# intel opencl gpu for openvino
|
||||
RUN bash -c "if [ \"$(uname -m)\" == \"x86_64\" ]; \
|
||||
then \
|
||||
apt-get update && apt-get install -y gpg-agent && \
|
||||
rm -f /usr/share/keyrings/intel-graphics.gpg && \
|
||||
curl -L https://repositories.intel.com/graphics/intel-graphics.key | gpg --dearmor --output /usr/share/keyrings/intel-graphics.gpg && \
|
||||
echo 'deb [arch=amd64,i386 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/graphics/ubuntu jammy arc' | tee /etc/apt/sources.list.d/intel.gpu.jammy.list && \
|
||||
apt-get -y update && \
|
||||
apt-get -y install intel-opencl-icd intel-media-va-driver-non-free && \
|
||||
apt-get -y dist-upgrade; \
|
||||
fi"
|
||||
RUN curl https://raw.githubusercontent.com/koush/scrypted/main/install/docker/install-intel-graphics.sh | bash
|
||||
|
||||
# python 3.9 from ppa.
|
||||
# 3.9 is the version with prebuilt support for tensorflow lite
|
||||
RUN add-apt-repository ppa:deadsnakes/ppa && \
|
||||
RUN add-apt-repository -y ppa:deadsnakes/ppa && \
|
||||
apt-get -y install \
|
||||
python3.9 \
|
||||
python3.9-dev \
|
||||
@@ -27,7 +18,6 @@ RUN add-apt-repository ppa:deadsnakes/ppa && \
|
||||
RUN rm -f /usr/lib/python**/EXTERNALLY-MANAGED
|
||||
|
||||
RUN python3.9 -m pip install --upgrade pip
|
||||
RUN python3.9 -m pip install --force-reinstall --no-binary :all: cffi
|
||||
RUN python3.9 -m pip install debugpy typing_extensions psutil
|
||||
|
||||
# Coral Edge TPU
|
||||
@@ -44,9 +34,6 @@ ENV SCRYPTED_INSTALL_PATH="/server"
|
||||
RUN test -f "/usr/bin/ffmpeg"
|
||||
ENV SCRYPTED_FFMPEG_PATH="/usr/bin/ffmpeg"
|
||||
|
||||
# changing this forces pip and npm to perform reinstalls.
|
||||
# if this base image changes, this version must be updated.
|
||||
ENV SCRYPTED_BASE_VERSION="20230727"
|
||||
ENV SCRYPTED_DOCKER_FLAVOR="full"
|
||||
|
||||
################################################################
|
||||
|
||||
@@ -22,10 +22,14 @@ RUN apt-get update && apt-get -y install \
|
||||
apt-get -y upgrade
|
||||
|
||||
ARG NODE_VERSION=18
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash -
|
||||
RUN apt-get install -y ca-certificates curl gnupg
|
||||
RUN mkdir -p /etc/apt/keyrings
|
||||
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor --yes -o /etc/apt/keyrings/nodesource.gpg
|
||||
RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_"$NODE_VERSION".x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
|
||||
RUN apt-get update && apt-get install -y nodejs
|
||||
|
||||
# python native
|
||||
RUN echo "Installing python."
|
||||
RUN apt-get -y install \
|
||||
python3 \
|
||||
python3-dev \
|
||||
@@ -35,44 +39,25 @@ RUN apt-get -y install \
|
||||
|
||||
# these are necessary for pillow-simd, additional on disk size is small
|
||||
# but could consider removing this.
|
||||
RUN echo "Installing pillow-simd dependencies."
|
||||
RUN apt-get -y install \
|
||||
libjpeg-dev zlib1g-dev
|
||||
|
||||
# plugins support fallback to pillow, but vips is faster.
|
||||
RUN apt-get -y install \
|
||||
libvips
|
||||
|
||||
# gstreamer native https://gstreamer.freedesktop.org/documentation/installing/on-linux.html?gi-language=c#install-gstreamer-on-ubuntu-or-debian
|
||||
RUN echo "Installing gstreamer."
|
||||
RUN apt-get -y install \
|
||||
gstreamer1.0-tools gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav gstreamer1.0-alsa \
|
||||
gstreamer1.0-vaapi
|
||||
|
||||
# python3 gstreamer bindings
|
||||
RUN echo "Installing gstreamer bindings."
|
||||
RUN apt-get -y install \
|
||||
python3-gst-1.0
|
||||
|
||||
# armv7l does not have wheels for any of these
|
||||
# and compile times would forever, if it works at all.
|
||||
# furthermore, it's possible to run 32bit docker on 64bit arm,
|
||||
# which causes weird behavior in python which looks at the arch version
|
||||
# which still reports 64bit, even if running in 32bit docker.
|
||||
# this scenario is not supported and will be reported at runtime.
|
||||
# this bit is not necessary on amd64, but leaving it for consistency.
|
||||
RUN apt-get -y install \
|
||||
python3-matplotlib \
|
||||
python3-numpy \
|
||||
python3-opencv \
|
||||
python3-pil \
|
||||
python3-skimage
|
||||
|
||||
# allow pip to install to system
|
||||
RUN rm -f /usr/lib/python**/EXTERNALLY-MANAGED
|
||||
|
||||
# pyvips is broken on x86 due to mismatch ffi
|
||||
# https://stackoverflow.com/questions/62658237/it-seems-that-the-version-of-the-libffi-library-seen-at-runtime-is-different-fro
|
||||
|
||||
RUN python3 -m pip install --upgrade pip
|
||||
RUN python3 -m pip install --force-reinstall --no-binary :all: cffi
|
||||
RUN python3 -m pip install debugpy typing_extensions psutil
|
||||
|
||||
################################################################
|
||||
|
||||
@@ -12,6 +12,26 @@ then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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 [ "$SERVICE_USER" = "root" ] && [ -z "$SERVICE_USER_ROOT" ]
|
||||
then
|
||||
readyn "Scrypted will store its files in the root user home directory. Running as a non-root user is recommended. Are you sure?"
|
||||
if [ "$yn" == "n" ]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Stopping existing service if it is running..."
|
||||
systemctl stop scrypted.service
|
||||
|
||||
@@ -49,6 +69,14 @@ ENV() {
|
||||
}
|
||||
|
||||
source <(curl -s https://raw.githubusercontent.com/koush/scrypted/main/install/docker/template/Dockerfile.full.header)
|
||||
if [ -z "$SCRYPTED_INSTALL_ENVIRONMENT" ]
|
||||
then
|
||||
SCRYPTED_INSTALL_ENVIRONMENT=local
|
||||
fi
|
||||
if [ "$SCRYPTED_INSTALL_ENVIRONMENT" = "lxc" ]
|
||||
then
|
||||
source <(curl -s https://raw.githubusercontent.com/koush/scrypted/main/install/docker/template/Dockerfile.full.footer)
|
||||
fi
|
||||
|
||||
if [ -z "$SERVICE_USER" ]
|
||||
then
|
||||
@@ -56,12 +84,6 @@ then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$SERVICE_USER" == "root" ]
|
||||
then
|
||||
echo "Scrypted SERVICE_USER root is not allowed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# this is not RUN as we do not care about the result
|
||||
USER_HOME=$(eval echo ~$SERVICE_USER)
|
||||
echo "Setting permissions on $USER_HOME/.scrypted"
|
||||
@@ -84,6 +106,7 @@ ExecStart=/usr/bin/npx -y scrypted serve
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
Environment="NODE_OPTIONS=$NODE_OPTIONS"
|
||||
Environment="SCRYPTED_INSTALL_ENVIRONMENT=$SCRYPTED_INSTALL_ENVIRONMENT"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -45,26 +45,11 @@ RUN brew install libvips
|
||||
# dlib
|
||||
RUN brew install cmake
|
||||
|
||||
### HACK WORKAROUND
|
||||
### https://github.com/koush/scrypted/issues/544
|
||||
|
||||
brew unpin gstreamer
|
||||
brew unpin gst-plugins-base
|
||||
brew unpin gst-plugins-good
|
||||
brew unpin gst-plugins-bad
|
||||
brew unpin gst-plugins-ugly
|
||||
brew unpin gst-libav
|
||||
brew unpin gst-python
|
||||
|
||||
### END HACK WORKAROUND
|
||||
|
||||
# seems to be necessary for python-codecs' pycairo dependency or something?
|
||||
RUN_IGNORE gobject-introspection libffi pkg-config
|
||||
|
||||
# gstreamer plugins
|
||||
RUN_IGNORE brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-libav
|
||||
# gst python bindings
|
||||
RUN_IGNORE brew install gst-python
|
||||
RUN_IGNORE brew install gstreamer
|
||||
|
||||
ARCH=$(arch)
|
||||
if [ "$ARCH" = "arm64" ]
|
||||
|
||||
84
install/local/install-scrypted-proxmox.sh
Normal file
84
install/local/install-scrypted-proxmox.sh
Normal file
@@ -0,0 +1,84 @@
|
||||
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
|
||||
}
|
||||
|
||||
cd /tmp
|
||||
SCRYPTED_VERSION=v0.80.0
|
||||
SCRYPTED_TAR_ZST=scrypted-$SCRYPTED_VERSION.tar.zst
|
||||
if [ -z "$VMID" ]
|
||||
then
|
||||
VMID=10443
|
||||
fi
|
||||
|
||||
echo "Downloading scrypted container backup."
|
||||
if [ ! -f "$SCRYPTED_TAR_ZST" ]
|
||||
then
|
||||
curl -O -L https://github.com/koush/scrypted/releases/download/$SCRYPTED_VERSION/scrypted.tar.zst
|
||||
mv scrypted.tar.zst $SCRYPTED_TAR_ZST
|
||||
fi
|
||||
|
||||
echo "Checking for existing container."
|
||||
pct config $VMID
|
||||
if [ "$?" == "0" ]
|
||||
then
|
||||
echo ""
|
||||
echo "Existing container $VMID found. Run this script with --force to overwrite the existing container."
|
||||
echo "This will wipe all existing data. Clone the existing container to retain the data, then reassign the owner of the scrypted volume after installation is complete."
|
||||
echo ""
|
||||
echo "bash $0 --force"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
pct restore $VMID $SCRYPTED_TAR_ZST $@
|
||||
|
||||
if [ "$?" != "0" ]
|
||||
then
|
||||
echo ""
|
||||
echo "pct restore failed"
|
||||
echo ""
|
||||
echo "This may be caused by the server's 'local' storage not supporting containers."
|
||||
echo "Try running this script again with a different storage device (local-lvm, local-zfs). For example:"
|
||||
echo ""
|
||||
echo "bash $0 --storage local-lvm"
|
||||
echo ""
|
||||
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
|
||||
echo "onboot: 1" >> $CONF
|
||||
else
|
||||
echo "$CONF not found? Start on boot must be enabled manually."
|
||||
fi
|
||||
|
||||
echo "Adding udev rule: /etc/udev/rules.d/65-scrypted.rules"
|
||||
readyn "Add udev rule for hardware acceleration? This may conflict with existing rules."
|
||||
if [ "$yn" == "y" ]
|
||||
then
|
||||
sh -c "echo 'SUBSYSTEM==\"apex\", MODE=\"0666\"' > /etc/udev/rules.d/65-scrypted.rules"
|
||||
sh -c "echo 'KERNEL==\"renderD128\", MODE=\"0666\"' >> /etc/udev/rules.d/65-scrypted.rules"
|
||||
sh -c "echo 'KERNEL==\"card0\", MODE=\"0666\"' >> /etc/udev/rules.d/65-scrypted.rules"
|
||||
sh -c "echo 'SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"1a6e\", ATTRS{idProduct}==\"089a\", MODE=\"0666\"' >> /etc/udev/rules.d/65-scrypted.rules"
|
||||
sh -c "echo 'SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"18d1\", ATTRS{idProduct}==\"9302\", MODE=\"0666\"' >> /etc/udev/rules.d/65-scrypted.rules"
|
||||
udevadm control --reload-rules && udevadm trigger
|
||||
fi
|
||||
|
||||
echo "Scrypted setup is complete and the container resources can be started."
|
||||
echo "Scrypted NVR users should provide at least 4 cores and 16GB RAM prior to starting."
|
||||
@@ -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
11
packages/auth-fetch/.gitignore
vendored
Normal 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
580
packages/auth-fetch/package-lock.json
generated
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
packages/auth-fetch/package.json
Normal file
23
packages/auth-fetch/package.json
Normal 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"
|
||||
}
|
||||
135
packages/auth-fetch/src/auth-fetch.ts
Normal file
135
packages/auth-fetch/src/auth-fetch.ts
Normal 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;
|
||||
}
|
||||
19
packages/auth-fetch/src/index.ts
Normal file
19
packages/auth-fetch/src/index.ts
Normal 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();
|
||||
16
packages/auth-fetch/tsconfig.json
Normal file
16
packages/auth-fetch/tsconfig.json
Normal 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/**/*"
|
||||
],
|
||||
}
|
||||
4
packages/cli/.vscode/launch.json
vendored
4
packages/cli/.vscode/launch.json
vendored
@@ -21,9 +21,7 @@
|
||||
],
|
||||
"preLaunchTask": "npm: build",
|
||||
"args": [
|
||||
"ffplay",
|
||||
"Baby Camera@192.168.2.109",
|
||||
"getVideoStream",
|
||||
"shell",
|
||||
],
|
||||
"sourceMaps": true,
|
||||
"resolveSourceMapLocations": [
|
||||
|
||||
748
packages/cli/package-lock.json
generated
748
packages/cli/package-lock.json
generated
@@ -1,37 +1,31 @@
|
||||
{
|
||||
"name": "scrypted",
|
||||
"version": "1.0.69",
|
||||
"version": "1.3.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "scrypted",
|
||||
"version": "1.0.69",
|
||||
"version": "1.3.6",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/client": "^1.1.43",
|
||||
"@scrypted/types": "^0.2.66",
|
||||
"adm-zip": "^0.5.10",
|
||||
"axios": "^0.21.4",
|
||||
"engine.io-client": "^5.2.0",
|
||||
"ip": "^1.1.8",
|
||||
"mkdirp": "^1.0.4",
|
||||
"@scrypted/client": "^1.3.3",
|
||||
"@scrypted/types": "^0.2.99",
|
||||
"engine.io-client": "^6.5.3",
|
||||
"readline-sync": "^1.4.10",
|
||||
"rimraf": "^3.0.2",
|
||||
"semver": "^7.3.8",
|
||||
"tslib": "^2.5.0"
|
||||
"semver": "^7.5.4",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"bin": {
|
||||
"scrypted": "dist/main.js"
|
||||
"scrypted": "dist/packages/cli/src/main.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mkdirp": "^1.0.2",
|
||||
"@types/node": "^18.14.2",
|
||||
"@types/readline-sync": "^1.4.4",
|
||||
"@types/rimraf": "^3.0.2",
|
||||
"@types/semver": "^7.3.13",
|
||||
"@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": "^4.9.5"
|
||||
"typescript": "^5.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
@@ -46,6 +40,22 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"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/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
@@ -71,69 +81,35 @@
|
||||
"@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",
|
||||
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/client": {
|
||||
"version": "1.1.43",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/client/-/client-1.1.43.tgz",
|
||||
"integrity": "sha512-qpeGdqFga/Fx51MoF3E0iBPCjE/SDEIVdGh8Ws5dqw38bxUJD264c9NsNyCguLKyYguErKTAWnQkzqhO0bUbaA==",
|
||||
"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.66",
|
||||
"axios": "^0.25.0",
|
||||
"engine.io-client": "^6.4.0",
|
||||
"rimraf": "^3.0.2"
|
||||
"@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/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/@scrypted/client/node_modules/engine.io-client": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.4.0.tgz",
|
||||
"integrity": "sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.0.3",
|
||||
"ws": "~8.11.0",
|
||||
"xmlhttprequest-ssl": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/client/node_modules/engine.io-parser": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz",
|
||||
"integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/client/node_modules/ws": {
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
"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.66",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.2.66.tgz",
|
||||
"integrity": "sha512-AL2iD7OmpqZlQMlpZKUBHpzL7H1IHhwKOi9uhRbVwG7EIDwenTspqtziH2Hyu0+XeCLf+gN69uQB6Qlz+QPf9A=="
|
||||
"version": "0.2.99",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.2.99.tgz",
|
||||
"integrity": "sha512-2J1FH7tpAW5X3rgA70gJ+z0HFM90c/tBA+JXdP1vI1d/0yVmh9TSxnHoCuADN4R2NQXHmoZ6Nbds9kKAQ/25XQ=="
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.0",
|
||||
@@ -164,57 +140,25 @@
|
||||
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/glob": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz",
|
||||
"integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/minimatch": "^5.1.2",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/minimatch": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
|
||||
"integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/mkdirp": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.2.tgz",
|
||||
"integrity": "sha512-o0K1tSO0Dx5X6xlU5F1D6625FawhC3dU3iqr25lluNv/+/QIVH8RLNEiVokgIZo+mz+87w/3Mkg/VvQS+J51fQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.2.tgz",
|
||||
"integrity": "sha512-1uEQxww3DaghA0RxqHx0O0ppVlo43pJhepY51OxuQIKHpjbnYLA7vcdwioNPzIqmC2u3I/dmylcqjlh0e7AyUA==",
|
||||
"dev": true
|
||||
"version": "20.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.4.tgz",
|
||||
"integrity": "sha512-wmyg8HUhcn6ACjsn8oKYjkN/zUzQeNtMy44weTJSM6p4MMzEOuKbA3OjJ267uPCOW7Xex9dyrNTful8XTQYoDA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/readline-sync": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/readline-sync/-/readline-sync-1.4.4.tgz",
|
||||
"integrity": "sha512-cFjVIoiamX7U6zkO2VPvXyTxbFDdiRo902IarJuPVxBhpDnXhwSaVE86ip+SCuyWBbEioKCkT4C88RNTxBM1Dw==",
|
||||
"version": "1.4.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/readline-sync/-/readline-sync-1.4.8.tgz",
|
||||
"integrity": "sha512-BL7xOf0yKLA6baAX6MMOnYkoflUyj/c7y3pqMRfU0va7XlwHAOTOIo4x55P/qLfMsuaYdJJKubToLqRVmRtRZA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/glob": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"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/acorn": {
|
||||
@@ -238,12 +182,26 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/adm-zip": {
|
||||
"version": "0.5.10",
|
||||
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz",
|
||||
"integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==",
|
||||
"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==",
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
"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==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/arg": {
|
||||
@@ -252,45 +210,34 @@
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true
|
||||
},
|
||||
"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/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"node_modules/base64-arraybuffer": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
|
||||
"integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/component-emitter": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
|
||||
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
|
||||
"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==",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||
"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=="
|
||||
},
|
||||
"node_modules/create-require": {
|
||||
"version": "1.1.1",
|
||||
@@ -298,6 +245,19 @@
|
||||
"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",
|
||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
"which": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
@@ -323,38 +283,40 @@
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"node_modules/engine.io-client": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.2.0.tgz",
|
||||
"integrity": "sha512-BcIBXGBkT7wKecwnfrSV79G2X5lSUSgeAGgoo60plXf8UsQEvCQww/KMwXSMhVjb98fFYNq20CC5eo8IOAPqsg==",
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz",
|
||||
"integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==",
|
||||
"dependencies": {
|
||||
"base64-arraybuffer": "0.1.4",
|
||||
"component-emitter": "~1.3.0",
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~4.0.1",
|
||||
"has-cors": "1.1.0",
|
||||
"parseqs": "0.0.6",
|
||||
"parseuri": "0.0.6",
|
||||
"ws": "~7.4.2",
|
||||
"xmlhttprequest-ssl": "~2.0.0",
|
||||
"yeast": "0.1.2"
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.11.0",
|
||||
"xmlhttprequest-ssl": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.3.tgz",
|
||||
"integrity": "sha512-xEAAY0msNnESNPc00e19y5heTPX4y/TJ36gr8t1voOaNmTojP9b3oK3BbJLFufW2XFPQaaijpFewm2g2Um3uqA==",
|
||||
"dependencies": {
|
||||
"base64-arraybuffer": "0.1.4"
|
||||
},
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz",
|
||||
"integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
"node": ">=10.0.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.4",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
|
||||
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@@ -370,53 +332,71 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"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==",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
"cross-spawn": "^7.0.0",
|
||||
"signal-exit": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/has-cors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
|
||||
"integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA=="
|
||||
},
|
||||
"node_modules/inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"node_modules/glob": {
|
||||
"version": "10.3.10",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
|
||||
"integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
|
||||
"dependencies": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
"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/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
"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==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ip": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
|
||||
"integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg=="
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
||||
},
|
||||
"node_modules/jackspeak": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
|
||||
"integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
|
||||
"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": "6.0.0",
|
||||
@@ -436,25 +416,25 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
|
||||
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
|
||||
"integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
@@ -462,30 +442,35 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
"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==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/parseqs": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
|
||||
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
|
||||
},
|
||||
"node_modules/parseuri": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
|
||||
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
|
||||
},
|
||||
"node_modules/path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"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==",
|
||||
"dependencies": {
|
||||
"lru-cache": "^9.1.1 || ^10.0.0",
|
||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry/node_modules/lru-cache": {
|
||||
"version": "10.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.3.tgz",
|
||||
"integrity": "sha512-B7gr+F6MkqB3uzINHXNctGieGsRTMwIBgxkp0yq/5BwcuDzD4A8wQpHQW6vDAm1uKSLQghmRdD9sKqf2vJ1cEg==",
|
||||
"engines": {
|
||||
"node": "14 || >=16.14"
|
||||
}
|
||||
},
|
||||
"node_modules/readline-sync": {
|
||||
@@ -497,23 +482,26 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
|
||||
"integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
"glob": "^10.3.7"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
"rimraf": "dist/esm/bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.3.8",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
|
||||
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
@@ -524,6 +512,124 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"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==",
|
||||
"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==",
|
||||
"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==",
|
||||
"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==",
|
||||
"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==",
|
||||
"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=="
|
||||
},
|
||||
"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==",
|
||||
"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==",
|
||||
"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==",
|
||||
"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==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||
@@ -568,40 +674,139 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
|
||||
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz",
|
||||
"integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
"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/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/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"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==",
|
||||
"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==",
|
||||
"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==",
|
||||
"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==",
|
||||
"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=="
|
||||
},
|
||||
"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==",
|
||||
"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==",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "7.4.6",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
|
||||
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
@@ -629,11 +834,6 @@
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"node_modules/yeast": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
|
||||
"integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg=="
|
||||
},
|
||||
"node_modules/yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "scrypted",
|
||||
"version": "1.0.69",
|
||||
"version": "1.3.6",
|
||||
"description": "",
|
||||
"main": "./dist/main.js",
|
||||
"main": "./dist/packages/cli/src/main.js",
|
||||
"bin": {
|
||||
"scrypted": "./dist/main.js"
|
||||
"scrypted": "./dist/packages/cli/src/main.js"
|
||||
},
|
||||
"scripts": {
|
||||
"prebuild": "rimraf dist",
|
||||
@@ -16,25 +16,19 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/client": "^1.1.43",
|
||||
"@scrypted/types": "^0.2.66",
|
||||
"adm-zip": "^0.5.10",
|
||||
"axios": "^0.21.4",
|
||||
"engine.io-client": "^5.2.0",
|
||||
"ip": "^1.1.8",
|
||||
"mkdirp": "^1.0.4",
|
||||
"@scrypted/client": "^1.3.3",
|
||||
"@scrypted/types": "^0.2.99",
|
||||
"engine.io-client": "^6.5.3",
|
||||
"readline-sync": "^1.4.10",
|
||||
"rimraf": "^3.0.2",
|
||||
"semver": "^7.3.8",
|
||||
"tslib": "^2.5.0"
|
||||
"semver": "^7.5.4",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mkdirp": "^1.0.2",
|
||||
"@types/node": "^18.14.2",
|
||||
"@types/readline-sync": "^1.4.4",
|
||||
"@types/rimraf": "^3.0.2",
|
||||
"@types/semver": "^7.3.13",
|
||||
"@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": "^4.9.5"
|
||||
"typescript": "^5.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
import readline from 'readline-sync';
|
||||
import https from 'https';
|
||||
import mkdirp from 'mkdirp';
|
||||
import { installServe, serveMain } from './service';
|
||||
import { connectScryptedClient } from '@scrypted/client';
|
||||
import { ScryptedMimeTypes, FFmpegInput } from '@scrypted/types';
|
||||
import semver from 'semver';
|
||||
import { FFmpegInput, ScryptedMimeTypes } from '@scrypted/types';
|
||||
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 { authHttpFetch } from '../../../common/src/http-auth-fetch';
|
||||
import { installServe, serveMain } from './service';
|
||||
import { connectShell } from './shell';
|
||||
|
||||
const httpsAgent = new https.Agent({
|
||||
rejectUnauthorized: false,
|
||||
@@ -54,17 +54,20 @@ async function doLogin(host: string) {
|
||||
});
|
||||
|
||||
const url = `https://${host}/login`;
|
||||
const response = await axios(Object.assign({
|
||||
const response = await authHttpFetch({
|
||||
method: 'GET',
|
||||
auth: {
|
||||
credential: {
|
||||
username,
|
||||
password,
|
||||
},
|
||||
url,
|
||||
httpsAgent,
|
||||
}, axiosConfig));
|
||||
rejectUnauthorized: false,
|
||||
responseType: 'json',
|
||||
});
|
||||
|
||||
mkdirp.sync(scryptedHome);
|
||||
fs.mkdirSync(scryptedHome, {
|
||||
recursive: true,
|
||||
});
|
||||
let login: LoginFile;
|
||||
try {
|
||||
login = JSON.parse(fs.readFileSync(loginPath).toString());
|
||||
@@ -76,7 +79,7 @@ async function doLogin(host: string) {
|
||||
login = {};
|
||||
login = login || {};
|
||||
|
||||
login[host] = response.data;
|
||||
login[host] = response.body;
|
||||
fs.writeFileSync(loginPath, JSON.stringify(login));
|
||||
return login;
|
||||
}
|
||||
@@ -100,12 +103,6 @@ async function getOrDoLogin(host: string): Promise<{
|
||||
return login[host];
|
||||
}
|
||||
|
||||
const axiosConfig: AxiosRequestConfig = {
|
||||
httpsAgent: new https.Agent({
|
||||
rejectUnauthorized: false
|
||||
})
|
||||
}
|
||||
|
||||
async function runCommand() {
|
||||
const [idOrName, optionalHost] = process.argv[3].split('@');
|
||||
const host = toIpAndPort(optionalHost || '127.0.0.1');
|
||||
@@ -117,9 +114,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);
|
||||
@@ -209,16 +203,34 @@ 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 authHttpFetch({
|
||||
method: 'POST',
|
||||
auth: {
|
||||
credential: {
|
||||
username: login.username,
|
||||
password: login.token,
|
||||
},
|
||||
url,
|
||||
}, axiosConfig));
|
||||
rejectUnauthorized: false,
|
||||
responseType: 'json',
|
||||
});
|
||||
|
||||
console.log('install successful. id:', response.data.id);
|
||||
console.log('install successful. id:', response.body.id);
|
||||
}
|
||||
else if (process.argv[2] === 'shell') {
|
||||
console.log = () => { };
|
||||
|
||||
const host = toIpAndPort(process.argv[3] || '127.0.0.1');
|
||||
const login = await getOrDoLogin(host);
|
||||
const sdk = await connectScryptedClient({
|
||||
baseUrl: `https://${host}`,
|
||||
pluginId: '@scrypted/core',
|
||||
username: login.username,
|
||||
password: login.token,
|
||||
});
|
||||
|
||||
const separator = process.argv.indexOf("--");
|
||||
const cmd = separator != -1 ? process.argv.slice(separator + 1) : [];
|
||||
await connectShell(sdk, ...cmd);
|
||||
}
|
||||
else {
|
||||
console.log('usage:');
|
||||
@@ -231,6 +243,7 @@ async function main() {
|
||||
console.log(' npx scrypted command name-or-id[@127.0.0.1[:10443]] method-name [...method-arguments]');
|
||||
console.log(' npx scrypted ffplay name-or-id[@127.0.0.1[:10443]] method-name [...method-arguments]');
|
||||
console.log(' npx scrypted create-cert-json /path/to/key.pem /path/to/cert.pem');
|
||||
console.log(' npx scrypted shell [127.0.0.1[:10443]] [-- cmd [...cmd-args]]');
|
||||
console.log();
|
||||
console.log('examples:');
|
||||
console.log(' npx scrypted install @scrypted/rtsp');
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
import child_process from 'child_process';
|
||||
import { once } from 'events';
|
||||
import fs from 'fs';
|
||||
import rimraf from 'rimraf';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import mkdirp from 'mkdirp';
|
||||
import semver from 'semver';
|
||||
|
||||
async function sleep(ms: number) {
|
||||
@@ -20,7 +18,12 @@ async function runCommand(command: string, ...args: string[]) {
|
||||
command += '.cmd';
|
||||
console.log('running', command, ...args);
|
||||
const cp = child_process.spawn(command, args, {
|
||||
stdio: 'inherit'
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
// https://github.com/lovell/sharp/blob/eefaa998725cf345227d94b40615e090495c6d09/lib/libvips.js#L115C19-L115C46
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS: 'true',
|
||||
},
|
||||
});
|
||||
await once(cp, 'exit');
|
||||
if (cp.exitCode)
|
||||
@@ -57,17 +60,26 @@ export function getInstallDir() {
|
||||
export function cwdInstallDir(): { volume: string, installDir: string } {
|
||||
const installDir = getInstallDir();
|
||||
const volume = path.join(installDir, 'volume');
|
||||
mkdirp.sync(volume);
|
||||
fs.mkdirSync(volume, {
|
||||
recursive: true,
|
||||
});
|
||||
process.chdir(installDir);
|
||||
return { volume, installDir };
|
||||
}
|
||||
|
||||
function rimrafSync(p: string) {
|
||||
fs.rmSync(p, {
|
||||
recursive: true,
|
||||
force: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function installServe(installVersion: string, ignoreError?: boolean) {
|
||||
const { installDir } = cwdInstallDir();
|
||||
const packageLockJson = path.join(installDir, 'package-lock.json');
|
||||
// apparently corrupted or old version of package-lock.json prevents upgrades, so
|
||||
// nuke it before installing.
|
||||
rimraf.sync(packageLockJson);
|
||||
rimrafSync(packageLockJson);
|
||||
|
||||
const installJson = path.join(installDir, 'install.json');
|
||||
try {
|
||||
@@ -78,7 +90,7 @@ export async function installServe(installVersion: string, ignoreError?: boolean
|
||||
catch (e) {
|
||||
const nodeModules = path.join(installDir, 'node_modules');
|
||||
console.log('Node version mismatch, missing, or corrupt. Clearing node_modules.');
|
||||
rimraf.sync(nodeModules);
|
||||
rimrafSync(nodeModules);
|
||||
}
|
||||
fs.writeFileSync(installJson, JSON.stringify({
|
||||
version: process.version,
|
||||
@@ -112,8 +124,8 @@ export async function serveMain(installVersion?: string) {
|
||||
console.log('cwd', process.cwd());
|
||||
|
||||
while (true) {
|
||||
rimraf.sync(EXIT_FILE);
|
||||
rimraf.sync(UPDATE_FILE);
|
||||
rimrafSync(EXIT_FILE);
|
||||
rimrafSync(UPDATE_FILE);
|
||||
|
||||
await startServer(installDir);
|
||||
|
||||
|
||||
90
packages/cli/src/shell.ts
Normal file
90
packages/cli/src/shell.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { DeviceProvider, ScryptedStatic, StreamService } from "@scrypted/types";
|
||||
import { createAsyncQueue } from '../../../common/src/async-queue';
|
||||
|
||||
export async function connectShell(sdk: ScryptedStatic, ...cmd: string[]) {
|
||||
const termSvc = await sdk.systemManager.getDeviceByName<DeviceProvider>("@scrypted/core").getDevice("terminalservice");
|
||||
if (!termSvc) {
|
||||
throw Error("@scrypted/core does not provide a Terminal Service");
|
||||
}
|
||||
|
||||
const termSvcDirect = await sdk.connectRPCObject<StreamService>(termSvc);
|
||||
const dataQueue = createAsyncQueue<Buffer>();
|
||||
const ctrlQueue = createAsyncQueue<any>();
|
||||
|
||||
if (process.stdin.isTTY) {
|
||||
process.stdin.setRawMode(true);
|
||||
} else {
|
||||
process.stdin.on("end", () => {
|
||||
ctrlQueue.enqueue({ eof: true });
|
||||
dataQueue.enqueue(Buffer.alloc(0));
|
||||
});
|
||||
}
|
||||
ctrlQueue.enqueue({ interactive: Boolean(process.stdin.isTTY), cmd: cmd });
|
||||
|
||||
const dim = { cols: process.stdout.columns, rows: process.stdout.rows };
|
||||
ctrlQueue.enqueue({ dim });
|
||||
|
||||
let bufferedLength = 0;
|
||||
const MAX_BUFFERED_LENGTH = 64000;
|
||||
process.stdin.on('data', async data => {
|
||||
bufferedLength += data.length;
|
||||
const promise = dataQueue.enqueue(data).then(() => bufferedLength -= data.length);
|
||||
if (bufferedLength >= MAX_BUFFERED_LENGTH) {
|
||||
process.stdin.pause();
|
||||
await promise;
|
||||
if (bufferedLength < MAX_BUFFERED_LENGTH)
|
||||
process.stdin.resume();
|
||||
}
|
||||
});
|
||||
|
||||
async function* generator() {
|
||||
while (true) {
|
||||
const ctrlBuffers = ctrlQueue.clear();
|
||||
if (ctrlBuffers.length) {
|
||||
for (const ctrl of ctrlBuffers) {
|
||||
if (ctrl.eof) {
|
||||
// flush the buffer before sending eof
|
||||
const dataBuffers = dataQueue.clear();
|
||||
const concat = Buffer.concat(dataBuffers);
|
||||
if (concat.length) {
|
||||
yield concat;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
process.stdout.on('resize', () => {
|
||||
const dim = { cols: process.stdout.columns, rows: process.stdout.rows };
|
||||
ctrlQueue.enqueue({ dim });
|
||||
dataQueue.enqueue(Buffer.alloc(0));
|
||||
});
|
||||
|
||||
try {
|
||||
for await (const message of await termSvcDirect.connectStream(generator())) {
|
||||
if (!message) {
|
||||
process.exit();
|
||||
}
|
||||
process.stdout.write(new Uint8Array(Buffer.from(message)));
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
} finally {
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
1
packages/client/.gitignore
vendored
1
packages/client/.gitignore
vendored
@@ -9,3 +9,4 @@ scrypted.db.bak
|
||||
.exit
|
||||
.update
|
||||
.venv
|
||||
.vscode/launch.json
|
||||
|
||||
25
packages/client/examples/connectRPCObject.ts
Normal file
25
packages/client/examples/connectRPCObject.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Camera, VideoCamera, VideoFrameGenerator } from '@scrypted/types';
|
||||
import { connectScryptedClient } from '../dist/packages/client/src';
|
||||
|
||||
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',
|
||||
});
|
||||
console.log('server version', sdk.serverVersion);
|
||||
|
||||
const office = sdk.systemManager.getDeviceByName<VideoCamera & Camera>("Office");
|
||||
const libav = sdk.systemManager.getDeviceByName<VideoFrameGenerator>("Libav");
|
||||
const mo = await office.getVideoStream();
|
||||
|
||||
const generator = await libav.generateVideoFrames(mo);
|
||||
const remote = await sdk.connectRPCObject!(generator);
|
||||
|
||||
for await (const frame of remote) {
|
||||
console.log(frame);
|
||||
}
|
||||
}
|
||||
|
||||
example();
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
234
packages/client/package-lock.json
generated
234
packages/client/package-lock.json
generated
@@ -1,23 +1,36 @@
|
||||
{
|
||||
"name": "@scrypted/client",
|
||||
"version": "1.1.57",
|
||||
"version": "1.3.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/client",
|
||||
"version": "1.1.57",
|
||||
"version": "1.3.4",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/types": "^0.2.95",
|
||||
"axios": "^0.25.0",
|
||||
"engine.io-client": "^6.5.2",
|
||||
"@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.1",
|
||||
"@types/node": "^20.8.4",
|
||||
"typescript": "^5.2.2"
|
||||
"@types/ip": "^1.1.3",
|
||||
"@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,31 +84,76 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/types": {
|
||||
"version": "0.2.95",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.2.95.tgz",
|
||||
"integrity": "sha512-gdSCsvGp1ZZowLOKP4CaxdTavnrE/bBfcfnvwsrPcxVRjbh+85fiNnXH2nX6L9uikAAPY3cIlcwbw3Dv1wzGQA=="
|
||||
"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.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ip/-/ip-1.1.1.tgz",
|
||||
"integrity": "sha512-/v+XZuKNBQHJi3dKeFt9LySLzWNkgmaYRtnFfg27Ag0MO9tQLzHUuAA8zOhPtbDvDGkcnZGr4pVZQPGNft/WYA==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ip/-/ip-1.1.3.tgz",
|
||||
"integrity": "sha512-64waoJgkXFTYnCYDUWgSATJ/dXEBanVkaP5d4Sbk7P6U7cTTMhxVyROTckc6JKdwCrgnAjZMn0k3177aQxtDEA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.4.tgz",
|
||||
"integrity": "sha512-ZVPnqU58giiCjSxjVUESDtdPk4QR5WQhhINbc9UBrKLU68MX5BF6kbQzTrkwbolyr0X8ChBpXfavr5mZFKZQ5A==",
|
||||
"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.25.1"
|
||||
"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": {
|
||||
@@ -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",
|
||||
@@ -172,9 +268,9 @@
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
|
||||
},
|
||||
"node_modules/engine.io-client": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.2.tgz",
|
||||
"integrity": "sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg==",
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz",
|
||||
"integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
@@ -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.2.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
||||
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
|
||||
"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",
|
||||
@@ -483,9 +628,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.25.3",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz",
|
||||
"integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==",
|
||||
"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/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": {
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/client",
|
||||
"version": "1.1.57",
|
||||
"version": "1.3.4",
|
||||
"description": "",
|
||||
"main": "dist/packages/client/src/index.js",
|
||||
"scripts": {
|
||||
@@ -12,14 +12,15 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/ip": "^1.1.1",
|
||||
"@types/node": "^20.8.4",
|
||||
"typescript": "^5.2.2"
|
||||
"@types/ip": "^1.1.3",
|
||||
"@types/node": "^20.10.8",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@scrypted/types": "^0.2.95",
|
||||
"axios": "^0.25.0",
|
||||
"engine.io-client": "^6.5.2",
|
||||
"@scrypted/types": "^0.3.4",
|
||||
"engine.io-client": "^6.5.3",
|
||||
"follow-redirects": "^1.15.4",
|
||||
"rimraf": "^5.0.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
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';
|
||||
@@ -14,6 +14,23 @@ import { createRpcDuplexSerializer, createRpcSerializer } from '../../../server/
|
||||
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;
|
||||
|
||||
function once(socket: IOClientSocket, event: 'open' | 'message') {
|
||||
@@ -56,7 +73,6 @@ export interface ScryptedConnectionOptions {
|
||||
local?: boolean;
|
||||
webrtc?: boolean;
|
||||
baseUrl?: string;
|
||||
axiosConfig?: AxiosRequestConfig;
|
||||
previousLoginResult?: ScryptedClientLoginResult;
|
||||
}
|
||||
|
||||
@@ -87,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() {
|
||||
@@ -119,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);
|
||||
@@ -158,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'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -707,6 +735,105 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
.map(id => systemManager.getDeviceById(id))
|
||||
.find(device => device.pluginId === '@scrypted/core' && device.nativeId === `user:${username}`);
|
||||
|
||||
const clusterPeers = new Map<number, Promise<RpcPeer>>();
|
||||
const ensureClusterPeer = (clusterObject: ClusterObject) => {
|
||||
let clusterPeerPromise = clusterPeers.get(clusterObject.port);
|
||||
if (!clusterPeerPromise) {
|
||||
clusterPeerPromise = (async () => {
|
||||
const eioPath = 'engine.io/connectRPCObject';
|
||||
const eioEndpoint = baseUrl ? new URL(eioPath, baseUrl).pathname : '/' + eioPath;
|
||||
const clusterPeerOptions = {
|
||||
path: eioEndpoint,
|
||||
query: {
|
||||
cacehBust,
|
||||
clusterObject: JSON.stringify(clusterObject),
|
||||
},
|
||||
withCredentials: true,
|
||||
extraHeaders,
|
||||
rejectUnauthorized: false,
|
||||
transports: options?.transports,
|
||||
};
|
||||
|
||||
const clusterPeerSocket = new eio.Socket(explicitBaseUrl, clusterPeerOptions);
|
||||
let peerReady = false;
|
||||
clusterPeerSocket.on('close', () => {
|
||||
clusterPeers.delete(clusterObject.port);
|
||||
if (!peerReady) {
|
||||
throw new Error("peer disconnected before setup completed");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await once(clusterPeerSocket, 'open');
|
||||
|
||||
const serializer = createRpcDuplexSerializer({
|
||||
write: data => clusterPeerSocket.send(data),
|
||||
});
|
||||
clusterPeerSocket.on('message', data => serializer.onData(Buffer.from(data)));
|
||||
|
||||
const clusterPeer = new RpcPeer(clientName || 'engine.io-client', "cluster-proxy", (message, reject, serializationContext) => {
|
||||
try {
|
||||
serializer.sendMessage(message, reject, serializationContext);
|
||||
}
|
||||
catch (e) {
|
||||
reject?.(e);
|
||||
}
|
||||
});
|
||||
serializer.setupRpcPeer(clusterPeer);
|
||||
clusterPeer.tags.localPort = sourcePeerId;
|
||||
peerReady = true;
|
||||
return clusterPeer;
|
||||
}
|
||||
catch (e) {
|
||||
console.error('failure ipc connect', e);
|
||||
clusterPeerSocket.close();
|
||||
throw e;
|
||||
}
|
||||
})();
|
||||
clusterPeers.set(clusterObject.port, clusterPeerPromise);
|
||||
}
|
||||
return clusterPeerPromise;
|
||||
};
|
||||
|
||||
const resolveObject = async (proxyId: string, sourcePeerPort: number) => {
|
||||
const sourcePeer = await clusterPeers.get(sourcePeerPort);
|
||||
if (sourcePeer?.remoteWeakProxies) {
|
||||
return Object.values(sourcePeer.remoteWeakProxies).find(
|
||||
v => v.deref()?.__cluster?.proxyId == proxyId
|
||||
)?.deref();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const connectRPCObject = async (value: any) => {
|
||||
const clusterObject: ClusterObject = value?.__cluster;
|
||||
if (!clusterObject) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const { port, proxyId } = clusterObject;
|
||||
|
||||
// check if object is already connected
|
||||
const resolved = await resolveObject(proxyId, port);
|
||||
if (resolved) {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
try {
|
||||
const clusterPeerPromise = ensureClusterPeer(clusterObject);
|
||||
const clusterPeer = await clusterPeerPromise;
|
||||
const connectRPCObject: ConnectRPCObject = await clusterPeer.getParam('connectRPCObject');
|
||||
const newValue = await connectRPCObject(clusterObject);
|
||||
if (!newValue)
|
||||
throw new Error('ipc object not found?');
|
||||
return newValue;
|
||||
}
|
||||
catch (e) {
|
||||
console.error('failure ipc', e);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
const ret: ScryptedClientStatic = {
|
||||
userId: userDevice?.id,
|
||||
serverVersion,
|
||||
@@ -736,7 +863,8 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
queryToken,
|
||||
authorization,
|
||||
cloudAddress,
|
||||
}
|
||||
},
|
||||
connectRPCObject,
|
||||
}
|
||||
|
||||
socket.on('close', () => {
|
||||
|
||||
35
plugins/alexa/package-lock.json
generated
35
plugins/alexa/package-lock.json
generated
@@ -1,24 +1,40 @@
|
||||
{
|
||||
"name": "@scrypted/alexa",
|
||||
"version": "0.2.7",
|
||||
"version": "0.2.10",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/alexa",
|
||||
"version": "0.2.7",
|
||||
"version": "0.2.10",
|
||||
"dependencies": {
|
||||
"axios": "^1.3.4",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scrypted/common": "../../common",
|
||||
"@scrypted/sdk": "../../sdk",
|
||||
"@types/node": "^18.4.2"
|
||||
}
|
||||
},
|
||||
"../../common": {
|
||||
"version": "1.0.1",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"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"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.9.0"
|
||||
}
|
||||
},
|
||||
"../../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.2.104",
|
||||
"version": "0.2.108",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
@@ -54,6 +70,13 @@
|
||||
"typedoc": "^0.23.21"
|
||||
}
|
||||
},
|
||||
"../common": {
|
||||
"extraneous": true
|
||||
},
|
||||
"node_modules/@scrypted/common": {
|
||||
"resolved": "../../common",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@scrypted/sdk": {
|
||||
"resolved": "../../sdk",
|
||||
"link": true
|
||||
@@ -70,9 +93,9 @@
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
|
||||
"integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
|
||||
"integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/alexa",
|
||||
"version": "0.2.8",
|
||||
"version": "0.2.10",
|
||||
"scripts": {
|
||||
"scrypted-setup-project": "scrypted-setup-project",
|
||||
"prescrypted-setup-project": "scrypted-package-json",
|
||||
@@ -39,6 +39,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.4.2",
|
||||
"@scrypted/sdk": "../../sdk"
|
||||
"@scrypted/sdk": "../../sdk",
|
||||
"@scrypted/common": "../../common"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
if (status === DeviceMixinStatus.Setup)
|
||||
await this.syncEndpoints();
|
||||
|
||||
if (status === DeviceMixinStatus.Setup || status === DeviceMixinStatus.AlreadySetup) {
|
||||
if (status === DeviceMixinStatus.Setup || status === DeviceMixinStatus.AlreadySetup) {
|
||||
|
||||
if (!this.devices.has(eventSource.id)) {
|
||||
this.devices.set(eventSource.id, eventSource);
|
||||
@@ -142,7 +142,7 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
await this.syncEndpoints();
|
||||
}
|
||||
|
||||
async deviceListen(eventSource: ScryptedDevice | undefined, eventDetails: EventDetails, eventData: any) : Promise<void> {
|
||||
async deviceListen(eventSource: ScryptedDevice | undefined, eventDetails: EventDetails, eventData: any): Promise<void> {
|
||||
if (!eventSource)
|
||||
return;
|
||||
|
||||
@@ -194,14 +194,14 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
|
||||
// nothing to report
|
||||
if (data.context === undefined && data.event.payload === undefined)
|
||||
return;
|
||||
|
||||
return;
|
||||
|
||||
data = await this.addAccessToken(data);
|
||||
|
||||
await this.postEvent(data);
|
||||
}
|
||||
|
||||
private async addAccessToken(data: any) : Promise<any> {
|
||||
private async addAccessToken(data: any): Promise<any> {
|
||||
const accessToken = await this.getAccessToken();
|
||||
|
||||
if (data.event === undefined)
|
||||
@@ -232,7 +232,7 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
'api.fe.amazonalexa.com'
|
||||
];
|
||||
|
||||
async getAlexaEndpoint() : Promise<string> {
|
||||
async getAlexaEndpoint(): Promise<string> {
|
||||
if (this.storageSettings.values.apiEndpoint)
|
||||
return this.storageSettings.values.apiEndpoint;
|
||||
|
||||
@@ -276,7 +276,7 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
});
|
||||
}
|
||||
|
||||
async getEndpoints() : Promise<DiscoveryEndpoint[]> {
|
||||
async getEndpoints(): Promise<DiscoveryEndpoint[]> {
|
||||
const endpoints: DiscoveryEndpoint[] = [];
|
||||
|
||||
for (const id of Object.keys(systemManager.getSystemState())) {
|
||||
@@ -284,7 +284,7 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
|
||||
if (!device.mixins?.includes(this.id))
|
||||
continue;
|
||||
|
||||
|
||||
const endpoint = await this.getEndpointForDevice(device);
|
||||
if (endpoint)
|
||||
endpoints.push(endpoint);
|
||||
@@ -319,7 +319,7 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
const endpoints = await this.getEndpoints();
|
||||
|
||||
if (!endpoints.length)
|
||||
return [];
|
||||
return [];
|
||||
|
||||
const accessToken = await this.getAccessToken();
|
||||
const data = {
|
||||
@@ -448,7 +448,7 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
self.console.warn(error?.response?.data);
|
||||
self.log.a(error?.response?.data?.error_description);
|
||||
break;
|
||||
|
||||
|
||||
case 'expired_token':
|
||||
self.console.warn(error?.response?.data);
|
||||
self.log.a(error?.response?.data?.error_description);
|
||||
@@ -480,9 +480,14 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
this.storageSettings.values.tokenInfo = grant;
|
||||
this.storageSettings.values.apiEndpoint = undefined;
|
||||
this.accessToken = undefined;
|
||||
|
||||
|
||||
const self = this;
|
||||
let accessToken = await this.getAccessToken().catch(reason => {
|
||||
let accessToken: any;
|
||||
|
||||
try {
|
||||
accessToken = await this.getAccessToken();
|
||||
}
|
||||
catch (reason) {
|
||||
self.console.error(`Failed to handle the AcceptGrant directive because ${reason}`);
|
||||
|
||||
this.storageSettings.values.tokenInfo = undefined;
|
||||
@@ -491,36 +496,23 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
|
||||
response.send(authErrorResponse("ACCEPT_GRANT_FAILED", `Failed to handle the AcceptGrant directive because ${reason}`, directive));
|
||||
|
||||
return undefined;
|
||||
});
|
||||
|
||||
if (accessToken !== undefined) {
|
||||
this.log.clearAlerts();
|
||||
|
||||
try {
|
||||
response.send({
|
||||
"event": {
|
||||
"header": {
|
||||
"namespace": "Alexa.Authorization",
|
||||
"name": "AcceptGrant.Response",
|
||||
"messageId": createMessageId(),
|
||||
"payloadVersion": "3"
|
||||
},
|
||||
"payload": {}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
this.console.error(`AcceptGrant.Response failed because ${error}`);
|
||||
|
||||
this.storageSettings.values.tokenInfo = undefined;
|
||||
this.storageSettings.values.apiEndpoint = undefined;
|
||||
this.accessToken = undefined;
|
||||
throw error;
|
||||
return;
|
||||
};
|
||||
this.log.clearAlerts();
|
||||
response.send({
|
||||
"event": {
|
||||
"header": {
|
||||
"namespace": "Alexa.Authorization",
|
||||
"name": "AcceptGrant.Response",
|
||||
"messageId": createMessageId(),
|
||||
"payloadVersion": "3"
|
||||
},
|
||||
"payload": {}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async getEndpointForDevice(device: ScryptedDevice) : Promise<DiscoveryEndpoint> {
|
||||
async getEndpointForDevice(device: ScryptedDevice): Promise<DiscoveryEndpoint> {
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
@@ -545,7 +537,7 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
};
|
||||
|
||||
let supportedEndpointHealths: any[] = [];
|
||||
|
||||
|
||||
if (device.interfaces.includes(ScryptedInterface.Online)) {
|
||||
supportedEndpointHealths.push({
|
||||
"name": "connectivity"
|
||||
@@ -632,8 +624,10 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
debug("received directive from alexa", mapName, body);
|
||||
|
||||
const handler = alexaHandlers.get(mapName);
|
||||
if (handler)
|
||||
return handler.apply(this, [request, response, directive]);
|
||||
if (handler) {
|
||||
await handler.apply(this, [request, response, directive]);
|
||||
return;
|
||||
}
|
||||
|
||||
const deviceHandler = alexaDeviceHandlers.get(mapName);
|
||||
|
||||
@@ -644,7 +638,8 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
return;
|
||||
}
|
||||
|
||||
return deviceHandler.apply(this, [request, response, directive, device]);
|
||||
await deviceHandler.apply(this, [request, response, directive, device]);
|
||||
return;
|
||||
} else {
|
||||
this.console.error(`no handler for: ${mapName}`);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { v4 as createMessageId } from 'uuid';
|
||||
import { AlexaHttpResponse, sendDeviceResponse } from "../../common";
|
||||
import { alexaDeviceHandlers } from "../../handlers";
|
||||
import { Response, WebRTCAnswerGeneratedForSessionEvent, WebRTCSessionConnectedEvent, WebRTCSessionDisconnectedEvent } from '../../alexa'
|
||||
import { Deferred } from '@scrypted/common/src/deferred';
|
||||
|
||||
export class AlexaSignalingSession implements RTCSignalingSession {
|
||||
constructor(public response: AlexaHttpResponse, public directive: any) {
|
||||
@@ -13,7 +14,8 @@ export class AlexaSignalingSession implements RTCSignalingSession {
|
||||
|
||||
__proxy_props: { options: RTCSignalingOptions; };
|
||||
options: RTCSignalingOptions;
|
||||
|
||||
remoteDescription = new Deferred<void>();
|
||||
|
||||
async getOptions(): Promise<RTCSignalingOptions> {
|
||||
return this.options;
|
||||
}
|
||||
@@ -39,11 +41,17 @@ export class AlexaSignalingSession implements RTCSignalingSession {
|
||||
}
|
||||
|
||||
async createLocalDescription(type: "offer" | "answer", setup: RTCAVSignalingSetup, sendIceCandidate: RTCSignalingSendIceCandidate): Promise<RTCSessionDescriptionInit> {
|
||||
if (type !== 'offer')
|
||||
throw new Error('Alexa only supports RTC offer');
|
||||
if (type !== 'offer') {
|
||||
const e = new Error('Alexa only supports RTC offer');
|
||||
this.remoteDescription.reject(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (sendIceCandidate)
|
||||
throw new Error("Alexa does not support trickle ICE");
|
||||
if (sendIceCandidate) {
|
||||
const e = new Error("Alexa does not support trickle ICE");
|
||||
this.remoteDescription.reject(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
return {
|
||||
type: type,
|
||||
@@ -67,15 +75,16 @@ export class AlexaSignalingSession implements RTCSignalingSession {
|
||||
},
|
||||
context: undefined
|
||||
};
|
||||
|
||||
|
||||
data.event.header.name = "AnswerGeneratedForSession";
|
||||
data.event.header.messageId = createMessageId();
|
||||
|
||||
|
||||
data.event.payload.answer = {
|
||||
format: 'SDP',
|
||||
value: description.sdp,
|
||||
};
|
||||
|
||||
this.remoteDescription.resolve();
|
||||
this.response.send(data);
|
||||
}
|
||||
}
|
||||
@@ -85,13 +94,14 @@ const sessionCache = new Map<string, RTCSessionControl>();
|
||||
alexaDeviceHandlers.set('Alexa.RTCSessionController/InitiateSessionWithOffer', async (request, response, directive: any, device: ScryptedDevice & RTCSignalingChannel) => {
|
||||
const { header, endpoint, payload } = directive;
|
||||
const { sessionId } = payload;
|
||||
|
||||
|
||||
const session = new AlexaSignalingSession(response, directive);
|
||||
const control = await device.startRTCSignalingSession(session);
|
||||
control.setPlayback({
|
||||
audio: true,
|
||||
video: false,
|
||||
})
|
||||
});
|
||||
await session.remoteDescription.promise;
|
||||
|
||||
sessionCache.set(sessionId, control);
|
||||
});
|
||||
@@ -115,13 +125,13 @@ alexaDeviceHandlers.set('Alexa.RTCSessionController/SessionConnected', async (re
|
||||
alexaDeviceHandlers.set('Alexa.RTCSessionController/SessionDisconnected', async (request, response, directive: any, device: ScryptedDevice) => {
|
||||
const { header, endpoint, payload } = directive;
|
||||
const { sessionId } = payload;
|
||||
|
||||
|
||||
const session = sessionCache.get(sessionId);
|
||||
if (session) {
|
||||
sessionCache.delete(sessionId);
|
||||
await session.endSession();
|
||||
}
|
||||
|
||||
|
||||
const data: WebRTCSessionDisconnectedEvent = {
|
||||
"event": {
|
||||
header,
|
||||
@@ -130,9 +140,9 @@ alexaDeviceHandlers.set('Alexa.RTCSessionController/SessionDisconnected', async
|
||||
},
|
||||
context: undefined
|
||||
};
|
||||
|
||||
|
||||
data.event.header.messageId = createMessageId();
|
||||
|
||||
|
||||
response.send(data);
|
||||
});
|
||||
|
||||
@@ -152,14 +162,14 @@ alexaDeviceHandlers.set('Alexa.SmartVision.ObjectDetectionSensor/SetObjectDetect
|
||||
},
|
||||
"context": {
|
||||
"properties": [{
|
||||
"namespace": "Alexa.SmartVision.ObjectDetectionSensor",
|
||||
"name": "objectDetectionClasses",
|
||||
"value": detectionTypes.classes.map(type => ({
|
||||
"imageNetClass": type
|
||||
})),
|
||||
timeOfSample: new Date().toISOString(),
|
||||
uncertaintyInMilliseconds: 0
|
||||
}]
|
||||
"namespace": "Alexa.SmartVision.ObjectDetectionSensor",
|
||||
"name": "objectDetectionClasses",
|
||||
"value": detectionTypes.classes.map(type => ({
|
||||
"imageNetClass": type
|
||||
})),
|
||||
timeOfSample: new Date().toISOString(),
|
||||
uncertaintyInMilliseconds: 0
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
179
plugins/amcrest/package-lock.json
generated
179
plugins/amcrest/package-lock.json
generated
@@ -1,25 +1,23 @@
|
||||
{
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.128",
|
||||
"version": "0.0.132",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.128",
|
||||
"version": "0.0.132",
|
||||
"license": "Apache",
|
||||
"dependencies": {
|
||||
"@koush/axios-digest-auth": "^0.8.5",
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"@types/multiparty": "^0.0.33",
|
||||
"multiparty": "^4.2.3"
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.16.18"
|
||||
"@types/node": "^20.10.8"
|
||||
}
|
||||
},
|
||||
"../../common": {
|
||||
"name": "@scrypted/common",
|
||||
"version": "1.0.1",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
@@ -27,14 +25,16 @@
|
||||
"@scrypted/server": "file:../server",
|
||||
"http-auth-utils": "^3.0.2",
|
||||
"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": {
|
||||
"version": "0.2.103",
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.3.4",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
@@ -69,15 +69,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/@scrypted/common": {
|
||||
"resolved": "../../common",
|
||||
"link": true
|
||||
@@ -86,150 +77,20 @@
|
||||
"resolved": "../../sdk",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@types/multiparty": {
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/multiparty/-/multiparty-0.0.33.tgz",
|
||||
"integrity": "sha512-Il6cJUpSqgojT7NxbVJUvXkCblm50/yEJYtblISDsNIeNYf4yMAhdizzidUk6h8pJ8yhwK/3Fkb+3Dwcgtwl8w==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.16.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.18.tgz",
|
||||
"integrity": "sha512-/aNaQZD0+iSBAGnvvN2Cx92HqE5sZCPZtx2TsK+4nvV23fFe09jVDvpArXr2j9DnYlzuU9WuoykDDc6wqvpNcw=="
|
||||
},
|
||||
"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": "0.21.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
|
||||
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
||||
"version": "20.10.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.8.tgz",
|
||||
"integrity": "sha512-f8nQs3cLxbAFc00vEU59yf9UyGUftkPaLGfvbVOIDdx2i1b8epBqj2aNGyP19fiyXWvlmZ7qC1XLjAzw/OKIeA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.0"
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||
"integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
|
||||
"integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
|
||||
"dependencies": {
|
||||
"depd": "~1.1.2",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": ">= 1.5.0 < 2",
|
||||
"toidentifier": "1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/multiparty": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/multiparty/-/multiparty-4.2.3.tgz",
|
||||
"integrity": "sha512-Ak6EUJZuhGS8hJ3c2fY6UW5MbkGUPMBEGd13djUzoY/BHqV/gTuFWtC6IuVA7A2+v3yjBS6c4or50xhzTQZImQ==",
|
||||
"dependencies": {
|
||||
"http-errors": "~1.8.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"uid-safe": "2.1.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/random-bytes": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
|
||||
"integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/setprototypeof": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||
"integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/uid-safe": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
|
||||
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
|
||||
"dependencies": {
|
||||
"random-bytes": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.128",
|
||||
"version": "0.0.132",
|
||||
"description": "Amcrest Plugin for Scrypted",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache",
|
||||
@@ -35,13 +35,10 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@koush/axios-digest-auth": "^0.8.5",
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"@types/multiparty": "^0.0.33",
|
||||
"multiparty": "^4.2.3"
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.16.18"
|
||||
"@types/node": "^20.10.8"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import AxiosDigestAuth from '@koush/axios-digest-auth';
|
||||
import { AuthFetchCredentialState, HttpFetchOptions, authHttpFetch } from '@scrypted/common/src/http-auth-fetch';
|
||||
import { Readable } from 'stream';
|
||||
import https from 'https';
|
||||
import { IncomingMessage } from 'http';
|
||||
import { amcrestHttpsAgent, getDeviceInfo } from './probe';
|
||||
import { getDeviceInfo } from './probe';
|
||||
|
||||
export enum AmcrestEvent {
|
||||
MotionStart = "Code=VideoMotion;action=Start",
|
||||
@@ -22,35 +20,42 @@ export enum AmcrestEvent {
|
||||
DahuaTalkPulse = "Code=_CallNoAnswer_;action=Pulse",
|
||||
}
|
||||
|
||||
|
||||
export class AmcrestCameraClient {
|
||||
digestAuth: AxiosDigestAuth;
|
||||
credential: AuthFetchCredentialState;
|
||||
|
||||
constructor(public ip: string, username: string, password: string, public console?: Console) {
|
||||
this.digestAuth = new AxiosDigestAuth({
|
||||
this.credential = {
|
||||
username,
|
||||
password,
|
||||
};
|
||||
}
|
||||
|
||||
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,
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
async reboot() {
|
||||
const response = await this.digestAuth.request({
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
method: "GET",
|
||||
responseType: 'text',
|
||||
const response = await this.request({
|
||||
url: `http://${this.ip}/cgi-bin/magicBox.cgi?action=reboot`,
|
||||
responseType: 'text',
|
||||
});
|
||||
return response.data as string;
|
||||
return response.body;
|
||||
}
|
||||
|
||||
async checkTwoWayAudio() {
|
||||
const response = await this.digestAuth.request({
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
method: "GET",
|
||||
responseType: 'text',
|
||||
const response = await this.request({
|
||||
url: `http://${this.ip}/cgi-bin/devAudioOutput.cgi?action=getCollect`,
|
||||
responseType: 'text',
|
||||
});
|
||||
return (response.data as string).includes('result=1');
|
||||
return response.body.includes('result=1');
|
||||
}
|
||||
|
||||
// appAutoStart=true
|
||||
@@ -61,33 +66,27 @@ export class AmcrestCameraClient {
|
||||
// updateSerial=IPC-AW46WN-S2
|
||||
// updateSerialCloudUpgrade=IPC-AW46WN-.....
|
||||
async getDeviceInfo() {
|
||||
return getDeviceInfo(this.digestAuth, this.ip);
|
||||
return getDeviceInfo(this.credential, this.ip);
|
||||
}
|
||||
|
||||
async jpegSnapshot(): Promise<Buffer> {
|
||||
|
||||
const response = await this.digestAuth.request({
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
method: "GET",
|
||||
responseType: 'arraybuffer',
|
||||
const response = await this.request({
|
||||
url: `http://${this.ip}/cgi-bin/snapshot.cgi`,
|
||||
timeout: 60000,
|
||||
});
|
||||
|
||||
return Buffer.from(response.data);
|
||||
return response.body;
|
||||
}
|
||||
|
||||
async listenEvents() {
|
||||
const url = `http://${this.ip}/cgi-bin/eventManager.cgi?action=attach&codes=[All]`;
|
||||
console.log('preparing event listener', url);
|
||||
|
||||
const response = await this.digestAuth.request({
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
method: "GET",
|
||||
responseType: 'stream',
|
||||
const response = await this.request({
|
||||
url,
|
||||
responseType: 'readable',
|
||||
});
|
||||
const stream = response.data as IncomingMessage;
|
||||
const stream = response.body;
|
||||
stream.socket.setKeepAlive(true);
|
||||
|
||||
stream.on('data', (buffer: Buffer) => {
|
||||
@@ -118,12 +117,12 @@ export class AmcrestCameraClient {
|
||||
async enableContinousRecording(channel: number) {
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const url = `http://${this.ip}/cgi-bin/configManager.cgi?action=setConfig&Record[${channel - 1}].TimeSection[${i}][0]=1 00:00:00-23:59:59`;
|
||||
const response = await this.digestAuth.request({
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
method: "POST",
|
||||
const response = await this.request({
|
||||
url,
|
||||
});
|
||||
this.console.log(response.data);
|
||||
method: 'POST',
|
||||
responseType: 'text',
|
||||
},);
|
||||
this.console.log(response.body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import { PassThrough, Readable, Stream } from "stream";
|
||||
import { OnvifIntercom } from "../../onvif/src/onvif-intercom";
|
||||
import { RtspProvider, RtspSmartCamera, UrlMediaStreamOptions } from "../../rtsp/src/rtsp";
|
||||
import { AmcrestCameraClient, AmcrestEvent } from "./amcrest-api";
|
||||
import { amcrestHttpsAgent } from './probe';
|
||||
|
||||
const { mediaManager } = sdk;
|
||||
|
||||
@@ -94,12 +93,11 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
|
||||
for (const element of deviceParameters) {
|
||||
try {
|
||||
const response = await this.getClient().digestAuth.request({
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
url: `http://${this.getHttpAddress()}/cgi-bin/magicBox.cgi?action=${element.action}`
|
||||
const response = await this.getClient().request({
|
||||
url: `http://${this.getHttpAddress()}/cgi-bin/magicBox.cgi?action=${element.action}`,
|
||||
responseType: 'text',
|
||||
});
|
||||
|
||||
const result = String(response.data).replace(element.replace, "").trim();
|
||||
const result = String(response.body).replace(element.replace, "").trim();
|
||||
deviceInfo[element.parameter] = result;
|
||||
}
|
||||
catch (e) {
|
||||
@@ -147,11 +145,11 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
if (![...params.keys()].length)
|
||||
return;
|
||||
|
||||
const response = await this.getClient().digestAuth.request({
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
url: `http://${this.getHttpAddress()}/cgi-bin/configManager.cgi?action=setConfig&${params}`
|
||||
const response = await this.getClient().request({
|
||||
url: `http://${this.getHttpAddress()}/cgi-bin/configManager.cgi?action=setConfig&${params}`,
|
||||
responseType: 'text',
|
||||
});
|
||||
this.console.log('reconfigure result', response.data);
|
||||
this.console.log('reconfigure result', response.body);
|
||||
}
|
||||
|
||||
getClient() {
|
||||
@@ -309,9 +307,6 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async takeSmartCameraPicture(option?: PictureOptions): Promise<MediaObject> {
|
||||
return this.createMediaObject(await this.getClient().jpegSnapshot(), 'image/jpeg');
|
||||
}
|
||||
@@ -334,7 +329,6 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
return this.storage.getItem('rtspChannel');
|
||||
}
|
||||
|
||||
|
||||
createRtspMediaStreamOptions(url: string, index: number) {
|
||||
const ret = super.createRtspMediaStreamOptions(url, index);
|
||||
ret.tool = 'scrypted';
|
||||
@@ -348,12 +342,11 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
this.videoStreamOptions = (async () => {
|
||||
let mas: string;
|
||||
try {
|
||||
const response = await client.digestAuth.request({
|
||||
const response = await client.request({
|
||||
url: `http://${this.getHttpAddress()}/cgi-bin/magicBox.cgi?action=getProductDefinition&name=MaxExtraStream`,
|
||||
responseType: 'text',
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
})
|
||||
mas = response.data.split('=')[1].trim();
|
||||
mas = response.body.split('=')[1].trim();
|
||||
this.storage.setItem('maxExtraStreams', mas.toString());
|
||||
}
|
||||
catch (e) {
|
||||
@@ -366,18 +359,16 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
const vsos = [...Array(maxExtraStreams + 1).keys()].map(subtype => this.createRtspMediaStreamOptions(`rtsp://${this.getRtspAddress()}/cam/realmonitor?channel=${channel}&subtype=${subtype}`, subtype));
|
||||
|
||||
try {
|
||||
const capResponse = await client.digestAuth.request({
|
||||
const capResponse = await client.request({
|
||||
url: `http://${this.getHttpAddress()}/cgi-bin/encode.cgi?action=getConfigCaps&channel=0`,
|
||||
responseType: 'text',
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
});
|
||||
this.console.log(capResponse.data);
|
||||
const encodeResponse = await client.digestAuth.request({
|
||||
this.console.log(capResponse.body);
|
||||
const encodeResponse = await client.request({
|
||||
url: `http://${this.getHttpAddress()}/cgi-bin/configManager.cgi?action=getConfig&name=Encode`,
|
||||
responseType: 'text',
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
});
|
||||
this.console.log(encodeResponse.data);
|
||||
this.console.log(encodeResponse.body);
|
||||
|
||||
for (let i = 0; i < vsos.length; i++) {
|
||||
const vso = vsos[i];
|
||||
@@ -392,16 +383,16 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
encName = `table.Encode[${channel - 1}].ExtraFormat[${i - 1}]`;
|
||||
}
|
||||
|
||||
const videoCodec = findValue(encodeResponse.data, encName, 'Video.Compression')
|
||||
const videoCodec = findValue(encodeResponse.body, encName, 'Video.Compression')
|
||||
?.replace('.', '')?.toLowerCase()?.trim();
|
||||
let audioCodec = findValue(encodeResponse.data, encName, 'Audio.Compression')
|
||||
let audioCodec = findValue(encodeResponse.body, encName, 'Audio.Compression')
|
||||
?.replace('.', '')?.toLowerCase()?.trim();
|
||||
if (audioCodec?.includes('aac'))
|
||||
audioCodec = 'aac';
|
||||
else if (audioCodec?.includes('g711a'))
|
||||
audioCodec = 'pcm_alaw';
|
||||
else if (audioCodec?.includes('g711u'))
|
||||
audioCodec = 'pcm_ulaw';
|
||||
audioCodec = 'pcm_mulaw';
|
||||
else if (audioCodec?.includes('g711'))
|
||||
audioCodec = 'pcm';
|
||||
|
||||
@@ -409,18 +400,18 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
vso.audio.codec = audioCodec;
|
||||
vso.video.codec = videoCodec;
|
||||
|
||||
const width = findValue(encodeResponse.data, encName, 'Video.Width');
|
||||
const height = findValue(encodeResponse.data, encName, 'Video.Height');
|
||||
const width = findValue(encodeResponse.body, encName, 'Video.Width');
|
||||
const height = findValue(encodeResponse.body, encName, 'Video.Height');
|
||||
if (width && height) {
|
||||
vso.video.width = parseInt(width);
|
||||
vso.video.height = parseInt(height);
|
||||
}
|
||||
|
||||
const bitrateOptions = findValue(capResponse.data, capName, 'Video.BitRateOptions');
|
||||
const bitrateOptions = findValue(capResponse.body, capName, 'Video.BitRateOptions');
|
||||
if (!bitrateOptions)
|
||||
continue;
|
||||
|
||||
const encodeOptions = findValue(encodeResponse.data, encName, 'Video.BitRate');
|
||||
const encodeOptions = findValue(encodeResponse.body, encName, 'Video.BitRate');
|
||||
if (!encodeOptions)
|
||||
continue;
|
||||
|
||||
@@ -504,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';
|
||||
@@ -514,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);
|
||||
|
||||
@@ -538,16 +548,15 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
// seems the dahua doorbells preferred 1024 chunks. should investigate adts
|
||||
// parsing and sending multipart chunks instead.
|
||||
const passthrough = new PassThrough();
|
||||
this.getClient().digestAuth.request({
|
||||
method: 'POST',
|
||||
this.getClient().request({
|
||||
url,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'Audio/AAC',
|
||||
'Content-Type': contentType,
|
||||
'Content-Length': '9999999'
|
||||
},
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
data: passthrough,
|
||||
});
|
||||
responseType: 'readable',
|
||||
}, passthrough);
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
@@ -598,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);
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import https from 'https';
|
||||
|
||||
export const amcrestHttpsAgent = new https.Agent({
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
import { AuthFetchCredentialState, authHttpFetch } from '@scrypted/common/src/http-auth-fetch';
|
||||
|
||||
// appAutoStart=true
|
||||
// deviceType=IP4M-1041B
|
||||
@@ -11,17 +7,16 @@ export const amcrestHttpsAgent = new https.Agent({
|
||||
// serialNumber=12345
|
||||
// updateSerial=IPC-AW46WN-S2
|
||||
|
||||
import AxiosDigestAuth from "@koush/axios-digest-auth";
|
||||
|
||||
// updateSerialCloudUpgrade=IPC-AW46WN-.....
|
||||
export async function getDeviceInfo(digestAuth: AxiosDigestAuth, address: string) {
|
||||
const response = await digestAuth.request({
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
method: "GET",
|
||||
responseType: 'text',
|
||||
export async function getDeviceInfo(credential: AuthFetchCredentialState, address: string) {
|
||||
const response = await authHttpFetch({
|
||||
credential,
|
||||
url: `http://${address}/cgi-bin/magicBox.cgi?action=getSystemInfo`,
|
||||
rejectUnauthorized: false,
|
||||
responseType: 'text',
|
||||
});
|
||||
const lines = (response.data as string).split('\n');
|
||||
const lines = response.body.split('\n');
|
||||
const vals: {
|
||||
[key: string]: string,
|
||||
} = {};
|
||||
|
||||
6
plugins/bticino/package-lock.json
generated
6
plugins/bticino/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/bticino",
|
||||
"version": "0.0.11",
|
||||
"version": "0.0.13",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/bticino",
|
||||
"version": "0.0.11",
|
||||
"version": "0.0.13",
|
||||
"dependencies": {
|
||||
"@slyoldfox/sip": "^0.0.6-1",
|
||||
"sdp": "^3.0.3",
|
||||
@@ -40,7 +40,7 @@
|
||||
},
|
||||
"../../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.2.103",
|
||||
"version": "0.3.2",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/bticino",
|
||||
"version": "0.0.11",
|
||||
"version": "0.0.13",
|
||||
"scripts": {
|
||||
"scrypted-setup-project": "scrypted-setup-project",
|
||||
"prescrypted-setup-project": "scrypted-package-json",
|
||||
|
||||
61
plugins/bticino/src/bticino-aswm-switch.ts
Normal file
61
plugins/bticino/src/bticino-aswm-switch.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { ScryptedDeviceBase, HttpRequest, HttpResponse, HttpRequestHandler, OnOff } from "@scrypted/sdk";
|
||||
import { BticinoSipCamera } from "./bticino-camera";
|
||||
import { VoicemailHandler } from "./bticino-voicemailHandler";
|
||||
|
||||
export class BticinoAswmSwitch extends ScryptedDeviceBase implements OnOff, HttpRequestHandler {
|
||||
private timeout : NodeJS.Timeout
|
||||
|
||||
constructor(private camera: BticinoSipCamera, private voicemailHandler : VoicemailHandler) {
|
||||
super( camera.nativeId + "-aswm-switch")
|
||||
this.timeout = setTimeout( () => this.syncStatus() , 5000 )
|
||||
}
|
||||
|
||||
turnOff(): Promise<void> {
|
||||
this.on = false
|
||||
return this.camera.turnOffAswm()
|
||||
}
|
||||
|
||||
turnOn(): Promise<void> {
|
||||
this.on = true
|
||||
return this.camera.turnOnAswm()
|
||||
}
|
||||
|
||||
syncStatus() {
|
||||
this.on = this.voicemailHandler.isAswmEnabled()
|
||||
this.timeout = setTimeout( () => this.syncStatus() , 5000 )
|
||||
}
|
||||
|
||||
cancelTimer() {
|
||||
if( this.timeout ) {
|
||||
clearTimeout(this.timeout)
|
||||
}
|
||||
}
|
||||
|
||||
public async onRequest(request: HttpRequest, response: HttpResponse): Promise<void> {
|
||||
if (request.url.endsWith('/disabled')) {
|
||||
this.on = false
|
||||
response.send('Success', {
|
||||
code: 200,
|
||||
});
|
||||
} else if( request.url.endsWith('/enabled') ) {
|
||||
this.on = true
|
||||
response.send('Success', {
|
||||
code: 200,
|
||||
});
|
||||
} else if( request.url.endsWith('/enable') ) {
|
||||
this.turnOn()
|
||||
response.send('Success', {
|
||||
code: 200,
|
||||
});
|
||||
} else if( request.url.endsWith('/disable') ) {
|
||||
this.turnOff()
|
||||
response.send('Success', {
|
||||
code: 200,
|
||||
});
|
||||
} else {
|
||||
response.send('Unsupported operation', {
|
||||
code: 400,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { closeQuiet, createBindZero, listenZeroSingleClient } from '@scrypted/common/src/listen-cluster';
|
||||
import { closeQuiet, createBindUdp, createBindZero, listenZeroSingleClient } from '@scrypted/common/src/listen-cluster';
|
||||
import { sleep } from '@scrypted/common/src/sleep';
|
||||
import { RtspServer } from '@scrypted/common/src/rtsp-server';
|
||||
import { addTrackControls } from '@scrypted/common/src/sdp-utils';
|
||||
import sdk, { BinarySensor, Camera, DeviceProvider, FFmpegInput, HttpRequest, HttpRequestHandler, HttpResponse, Intercom, MediaObject, MediaStreamUrl, PictureOptions, Reboot, ResponseMediaStreamOptions, ScryptedDevice, ScryptedDeviceBase, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera, VideoClip, VideoClipOptions, VideoClips } from '@scrypted/sdk';
|
||||
import { addTrackControls, parseSdp } from '@scrypted/common/src/sdp-utils';
|
||||
import sdk, { BinarySensor, Camera, DeviceProvider, FFmpegInput, HttpRequest, HttpRequestHandler, HttpResponse, Intercom, MediaObject, MediaStreamUrl, MotionSensor, PictureOptions, Reboot, ResponseMediaStreamOptions, ScryptedDeviceBase, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera, VideoClip, VideoClipOptions, VideoClips } from '@scrypted/sdk';
|
||||
import { SipCallSession } from '../../sip/src/sip-call-session';
|
||||
import { RtpDescription } from '../../sip/src/rtp-utils';
|
||||
import { RtpDescription, getPayloadType, getSequenceNumber, isRtpMessagePayloadType, isStunMessage } from '../../sip/src/rtp-utils';
|
||||
import { VoicemailHandler } from './bticino-voicemailHandler';
|
||||
import { CompositeSipMessageHandler } from '../../sip/src/compositeSipMessageHandler';
|
||||
import { SipHelper } from './sip-helper';
|
||||
@@ -16,22 +16,22 @@ import { BticinoSipLock } from './bticino-lock';
|
||||
import { ffmpegLogInitialOutput, safeKillFFmpeg, safePrintFFmpegArguments } from '@scrypted/common/src/media-helpers';
|
||||
import { PersistentSipManager } from './persistent-sip-manager';
|
||||
import { InviteHandler } from './bticino-inviteHandler';
|
||||
import { SipRequest } from '../../sip/src/sip-manager';
|
||||
import { SipOptions, SipRequest } from '../../sip/src/sip-manager';
|
||||
|
||||
import { get } from 'http'
|
||||
import { ControllerApi } from './c300x-controller-api';
|
||||
import { BticinoAswmSwitch } from './bticino-aswm-switch';
|
||||
import { BticinoMuteSwitch } from './bticino-mute-switch';
|
||||
|
||||
const STREAM_TIMEOUT = 65000;
|
||||
const { mediaManager } = sdk;
|
||||
|
||||
export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor, HttpRequestHandler, VideoClips, Reboot {
|
||||
export class BticinoSipCamera extends ScryptedDeviceBase implements MotionSensor, DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor, HttpRequestHandler, VideoClips, Reboot {
|
||||
|
||||
private session: SipCallSession
|
||||
private remoteRtpDescription: Promise<RtpDescription>
|
||||
private audioOutForwarder: dgram.Socket
|
||||
private audioOutProcess: ChildProcess
|
||||
private currentMedia: FFmpegInput | MediaStreamUrl
|
||||
private currentMediaMimeType: string
|
||||
private refreshTimeout: NodeJS.Timeout
|
||||
public requestHandlers: CompositeSipMessageHandler = new CompositeSipMessageHandler()
|
||||
public incomingCallRequest : SipRequest
|
||||
@@ -39,16 +39,21 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid
|
||||
private voicemailHandler : VoicemailHandler = new VoicemailHandler(this)
|
||||
private inviteHandler : InviteHandler = new InviteHandler(this)
|
||||
private controllerApi : ControllerApi = new ControllerApi(this)
|
||||
private muteSwitch : BticinoMuteSwitch
|
||||
private aswmSwitch : BticinoAswmSwitch
|
||||
private deferredCleanup
|
||||
private currentMediaObject : Promise<MediaObject>
|
||||
private lastImageRefresh : number
|
||||
//TODO: randomize this
|
||||
private keyAndSalt : string = "/qE7OPGKp9hVGALG2KcvKWyFEZfSSvm7bYVDjT8X"
|
||||
//private decodedSrtpOptions : SrtpOptions = decodeSrtpOptions( this.keyAndSalt )
|
||||
private persistentSipManager : PersistentSipManager
|
||||
public doorbellWebhookUrl : string
|
||||
public doorbellLockWebhookUrl : string
|
||||
private cachedImage : Buffer
|
||||
|
||||
constructor(nativeId: string, public provider: BticinoSipPlugin) {
|
||||
super(nativeId)
|
||||
|
||||
this.requestHandlers.add( this.voicemailHandler ).add( this.inviteHandler )
|
||||
this.persistentSipManager = new PersistentSipManager( this );
|
||||
(async() => {
|
||||
@@ -63,10 +68,50 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid
|
||||
|
||||
get(`http://${c300x}:8080/reboot?now`, (res) => {
|
||||
console.log("Reboot API result: " + res.statusCode)
|
||||
});
|
||||
}).on('error', (error) => {
|
||||
this.console.error(error)
|
||||
reject(error)
|
||||
} ).end();
|
||||
})
|
||||
}
|
||||
|
||||
muteRinger(mute : boolean): Promise<void> {
|
||||
return new Promise<void>( (resolve,reject ) => {
|
||||
let c300x = SipHelper.getIntercomIp(this)
|
||||
|
||||
get(`http://${c300x}:8080/mute?raw=true&enable=` + mute, (res) => {
|
||||
console.log("Mute API result: " + res.statusCode)
|
||||
}).on('error', (error) => {
|
||||
this.console.error(error)
|
||||
reject(error)
|
||||
} ).end();
|
||||
})
|
||||
}
|
||||
|
||||
muteStatus(): Promise<boolean> {
|
||||
return new Promise<boolean>( (resolve,reject ) => {
|
||||
let c300x = SipHelper.getIntercomIp(this)
|
||||
|
||||
get(`http://${c300x}:8080/mute?status=true&raw=true`, (res) => {
|
||||
let rawData = '';
|
||||
res.on('data', (chunk) => { rawData += chunk; })
|
||||
res.on('error', (error) => this.console.log(error))
|
||||
res.on('end', () => {
|
||||
try {
|
||||
return resolve(JSON.parse(rawData))
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
reject(e.message)
|
||||
|
||||
}
|
||||
})
|
||||
}).on('error', (error) => {
|
||||
this.console.error(error)
|
||||
reject(error)
|
||||
} ).end();
|
||||
})
|
||||
}
|
||||
|
||||
getVideoClips(options?: VideoClipOptions): Promise<VideoClip[]> {
|
||||
return new Promise<VideoClip[]>( (resolve,reject ) => {
|
||||
let c300x = SipHelper.getIntercomIp(this)
|
||||
@@ -95,7 +140,10 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid
|
||||
console.error(e.message);
|
||||
}
|
||||
})
|
||||
});
|
||||
}).on('error', (error) => {
|
||||
this.console.error(error)
|
||||
reject(error)
|
||||
} ).end(); ;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -132,8 +180,34 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid
|
||||
} )
|
||||
}
|
||||
|
||||
turnOnAswm() : Promise<void> {
|
||||
return this.persistentSipManager.enable().then( (sipCall) => {
|
||||
sipCall.message( "*8*91##" )
|
||||
} )
|
||||
}
|
||||
|
||||
turnOffAswm() : Promise<void> {
|
||||
return this.persistentSipManager.enable().then( (sipCall) => {
|
||||
sipCall.message( "*8*92##" )
|
||||
} )
|
||||
}
|
||||
|
||||
async takePicture(option?: PictureOptions): Promise<MediaObject> {
|
||||
throw new Error("The SIP doorbell camera does not provide snapshots. Install the Snapshot Plugin if snapshots are available via an URL.");
|
||||
const thumbnailCacheTime : number = parseInt( this.storage?.getItem('thumbnailCacheTime') ) * 1000 || 300000
|
||||
const now = new Date().getTime()
|
||||
if( !this.lastImageRefresh || this.lastImageRefresh + thumbnailCacheTime < now ) {
|
||||
// get a proxy object to make sure we pass prebuffer when already watching a stream
|
||||
let cam : VideoCamera = sdk.systemManager.getDeviceById<VideoCamera>(this.id)
|
||||
let vs : MediaObject = await cam.getVideoStream()
|
||||
let buf : Buffer = await mediaManager.convertMediaObjectToBuffer(vs, 'image/jpeg');
|
||||
this.cachedImage = buf
|
||||
this.lastImageRefresh = new Date().getTime()
|
||||
this.console.log(`Camera picture updated and cached: ${this.lastImageRefresh} + cache time: ${thumbnailCacheTime} < ${now}`)
|
||||
|
||||
} else {
|
||||
this.console.log(`Not refreshing camera picture: ${this.lastImageRefresh} + cache time: ${thumbnailCacheTime} < ${now}`)
|
||||
}
|
||||
return mediaManager.createMediaObject(this.cachedImage, 'image/jpeg')
|
||||
}
|
||||
|
||||
async getPictureOptions(): Promise<PictureOptions[]> {
|
||||
@@ -149,8 +223,17 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid
|
||||
}
|
||||
|
||||
async startIntercom(media: MediaObject): Promise<void> {
|
||||
if (!this.session)
|
||||
throw new Error("not in call");
|
||||
if (!this.session) {
|
||||
const cleanup = () => {
|
||||
this.console.log("STARTINTERCOM CLEANUP CALLED: " + this.session )
|
||||
this.session?.stop()
|
||||
this.session = undefined
|
||||
this.deferredCleanup()
|
||||
this.console.log("STARTINTERCOM CLEANUP ENDED")
|
||||
}
|
||||
this.session = await this.callIntercom( cleanup )
|
||||
}
|
||||
|
||||
|
||||
this.stopIntercom();
|
||||
|
||||
@@ -223,27 +306,24 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid
|
||||
throw new Error('Please configure from/to/domain settings')
|
||||
}
|
||||
|
||||
if (options?.metadata?.refreshAt) {
|
||||
if (!this.currentMedia?.mediaStreamOptions)
|
||||
throw new Error("no stream to refresh");
|
||||
|
||||
const currentMedia = this.currentMedia
|
||||
currentMedia.mediaStreamOptions.refreshAt = Date.now() + STREAM_TIMEOUT;
|
||||
currentMedia.mediaStreamOptions.metadata = {
|
||||
refreshAt: currentMedia.mediaStreamOptions.refreshAt
|
||||
};
|
||||
this.resetStreamTimeout()
|
||||
return mediaManager.createMediaObject(currentMedia, this.currentMediaMimeType)
|
||||
}
|
||||
|
||||
this.console.log("Before stopping session")
|
||||
this.stopSession();
|
||||
const { clientPromise: playbackPromise, port: playbackPort, url: clientUrl } = await listenZeroSingleClient()
|
||||
this.console.log("After stopping session")
|
||||
|
||||
const playbackUrl = clientUrl
|
||||
let rebroadcastEnabled = this.interfaces?.includes( "mixin:@scrypted/prebuffer-mixin")
|
||||
|
||||
const { clientPromise: playbackPromise, port: playbackPort } = await listenZeroSingleClient()
|
||||
|
||||
const playbackUrl = `rtsp://127.0.0.1:${playbackPort}`
|
||||
|
||||
this.console.log("PLAYBACKURL: " +playbackUrl)
|
||||
|
||||
playbackPromise.then(async (client) => {
|
||||
client.setKeepAlive(true, 10000)
|
||||
|
||||
let sip: SipCallSession
|
||||
let audioSplitter
|
||||
let videoSplitter
|
||||
try {
|
||||
if( !this.incomingCallRequest ) {
|
||||
// If this is a "view" call, update the stream endpoint to send it only to "us"
|
||||
@@ -252,61 +332,37 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid
|
||||
}
|
||||
|
||||
let rtsp: RtspServer;
|
||||
|
||||
const cleanup = () => {
|
||||
this.console.log("CLEANUP CALLED")
|
||||
client.destroy();
|
||||
if (this.session === sip)
|
||||
this.session = undefined
|
||||
try {
|
||||
this.log.d('cleanup(): stopping sip session.')
|
||||
sip.stop()
|
||||
sip?.stop()
|
||||
this.currentMediaObject = undefined
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
audioSplitter?.server?.close()
|
||||
videoSplitter?.server?.close()
|
||||
rtsp?.destroy()
|
||||
this.console.log("CLEANUP ENDED")
|
||||
this.deferredCleanup = undefined
|
||||
this.remoteRtpDescription = undefined
|
||||
}
|
||||
this.deferredCleanup = cleanup
|
||||
|
||||
client.on('close', cleanup)
|
||||
client.on('error', cleanup)
|
||||
|
||||
let sipOptions = SipHelper.sipOptions( this )
|
||||
|
||||
sip = await this.persistentSipManager.session( sipOptions );
|
||||
// Validate this sooner
|
||||
if( !sip ) return Promise.reject("Cannot create session")
|
||||
|
||||
sip.onCallEnded.subscribe(cleanup)
|
||||
|
||||
// Call the C300X
|
||||
this.remoteRtpDescription = sip.callOrAcceptInvite(
|
||||
( audio ) => {
|
||||
return [
|
||||
//TODO: Payload types are hardcoded
|
||||
`m=audio 65000 RTP/SAVP 110`,
|
||||
`a=rtpmap:110 speex/8000`,
|
||||
`a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`,
|
||||
]
|
||||
}, ( video ) => {
|
||||
if( false ) {
|
||||
//TODO: implement later
|
||||
return [
|
||||
`m=video 0 RTP/SAVP 0`
|
||||
]
|
||||
} else {
|
||||
return [
|
||||
//TODO: Payload types are hardcoded
|
||||
`m=video 65002 RTP/SAVP 96`,
|
||||
`a=rtpmap:96 H264/90000`,
|
||||
`a=fmtp:96 profile-level-id=42801F`,
|
||||
`a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`,
|
||||
'a=recvonly'
|
||||
]
|
||||
}
|
||||
}, this.incomingCallRequest );
|
||||
|
||||
this.incomingCallRequest = undefined
|
||||
if( !rebroadcastEnabled || (rebroadcastEnabled && !this.incomingCallRequest ) ) {
|
||||
sip = await this.callIntercom( cleanup )
|
||||
}
|
||||
|
||||
//let sdp: string = replacePorts(this.remoteRtpDescription.sdp, 0, 0 )
|
||||
let sdp : string = [
|
||||
let sdp : string = [
|
||||
"v=0",
|
||||
"m=audio 5000 RTP/AVP 110",
|
||||
"c=IN IP4 127.0.0.1",
|
||||
@@ -318,42 +374,141 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid
|
||||
//sdp = sdp.replaceAll(/a=crypto\:1.*/g, '')
|
||||
//sdp = sdp.replaceAll(/RTP\/SAVP/g, 'RTP\/AVP')
|
||||
//sdp = sdp.replaceAll('\r\n\r\n', '\r\n')
|
||||
sdp = addTrackControls(sdp)
|
||||
sdp = sdp.split('\n').filter(line => !line.includes('a=rtcp-mux')).join('\n')
|
||||
if( sipOptions.debugSip )
|
||||
this.log.d('SIP: Updated SDP:\n' + sdp);
|
||||
|
||||
client.write(sdp)
|
||||
client.end()
|
||||
let vseq = 0;
|
||||
let vseen = 0;
|
||||
let vlost = 0;
|
||||
let aseq = 0;
|
||||
let aseen = 0;
|
||||
let alost = 0;
|
||||
|
||||
sdp = addTrackControls(sdp);
|
||||
sdp = sdp.split('\n').filter(line => !line.includes('a=rtcp-mux')).join('\n');
|
||||
this.console.log('proposed sdp', sdp);
|
||||
|
||||
this.console.log("================= AUDIOSPLITTER CREATING.... ============" )
|
||||
audioSplitter = await createBindUdp(5000)
|
||||
this.console.log("================= AUDIOSPLITTER CREATED ============" )
|
||||
audioSplitter.server.on('close', () => {
|
||||
this.console.log("================= CLOSED AUDIOSPLITTER ================")
|
||||
audioSplitter = undefined
|
||||
})
|
||||
this.console.log("================= VIDEOSPLITTER CREATING.... ============" )
|
||||
videoSplitter = await createBindUdp(5002)
|
||||
this.console.log("================= VIDEOSPLITTER CREATED.... ============" )
|
||||
videoSplitter.server.on('close', () => {
|
||||
this.console.log("================= CLOSED VIDEOSPLITTER ================")
|
||||
videoSplitter = undefined
|
||||
})
|
||||
|
||||
rtsp = new RtspServer(client, sdp, false);
|
||||
|
||||
const parsedSdp = parseSdp(rtsp.sdp);
|
||||
const videoTrack = parsedSdp.msections.find(msection => msection.type === 'video').control;
|
||||
const audioTrack = parsedSdp.msections.find(msection => msection.type === 'audio').control;
|
||||
rtsp.console = this.console;
|
||||
|
||||
await rtsp.handlePlayback();
|
||||
|
||||
this.session = sip
|
||||
|
||||
videoSplitter.server.on('message', (message, rinfo) => {
|
||||
if ( !isStunMessage(message)) {
|
||||
const isRtpMessage = isRtpMessagePayloadType(getPayloadType(message));
|
||||
if (!isRtpMessage)
|
||||
return;
|
||||
vseen++;
|
||||
try {
|
||||
rtsp.sendTrack(videoTrack, message, !isRtpMessage);
|
||||
} catch(e ) {
|
||||
this.console.log(e)
|
||||
}
|
||||
|
||||
const seq = getSequenceNumber(message);
|
||||
if (seq !== (vseq + 1) % 0x0FFFF)
|
||||
vlost++;
|
||||
vseq = seq;
|
||||
}
|
||||
});
|
||||
|
||||
audioSplitter.server.on('message', (message, rinfo ) => {
|
||||
if ( !isStunMessage(message)) {
|
||||
const isRtpMessage = isRtpMessagePayloadType(getPayloadType(message));
|
||||
if (!isRtpMessage)
|
||||
return;
|
||||
aseen++;
|
||||
try {
|
||||
rtsp.sendTrack(audioTrack, message, !isRtpMessage);
|
||||
} catch(e) {
|
||||
this.console.log(e)
|
||||
}
|
||||
const seq = getSequenceNumber(message);
|
||||
if (seq !== (aseq + 1) % 0x0FFFF)
|
||||
alost++;
|
||||
aseq = seq;
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await rtsp.handleTeardown();
|
||||
this.console.log('rtsp client ended');
|
||||
} catch (e) {
|
||||
this.console.log('rtsp client ended ungracefully', e);
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
this.console.error(e)
|
||||
sip?.stop()
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
this.resetStreamTimeout();
|
||||
|
||||
const mediaStreamOptions = Object.assign(this.getSipMediaStreamOptions(), {
|
||||
refreshAt: Date.now() + STREAM_TIMEOUT,
|
||||
});
|
||||
|
||||
const ffmpegInput: FFmpegInput = {
|
||||
url: undefined,
|
||||
container: 'sdp',
|
||||
mediaStreamOptions,
|
||||
inputArguments: [
|
||||
'-f', 'sdp',
|
||||
'-i', playbackUrl,
|
||||
],
|
||||
const mediaStreamUrl: MediaStreamUrl = {
|
||||
url: playbackUrl,
|
||||
mediaStreamOptions: this.getSipMediaStreamOptions(),
|
||||
};
|
||||
this.currentMedia = ffmpegInput;
|
||||
this.currentMediaMimeType = ScryptedMimeTypes.FFmpegInput;
|
||||
|
||||
return mediaManager.createFFmpegMediaObject(ffmpegInput);
|
||||
sleep(2500).then( () => this.takePicture() )
|
||||
|
||||
this.currentMediaObject = mediaManager.createMediaObject(mediaStreamUrl, ScryptedMimeTypes.MediaStreamUrl);
|
||||
// Invalidate any cached image and take a picture after some seconds to take into account the opening of the lens
|
||||
this.lastImageRefresh = undefined
|
||||
return this.currentMediaObject
|
||||
}
|
||||
|
||||
async callIntercom( cleanup ) : Promise<SipCallSession> {
|
||||
let sipOptions : SipOptions = SipHelper.sipOptions( this )
|
||||
|
||||
let sip : SipCallSession = await this.persistentSipManager.session( sipOptions );
|
||||
// Validate this sooner
|
||||
if( !sip ) return Promise.reject("Cannot create session")
|
||||
|
||||
sip.onCallEnded.subscribe(cleanup)
|
||||
|
||||
// Call the C300X
|
||||
this.remoteRtpDescription = sip.callOrAcceptInvite(
|
||||
( audio ) => {
|
||||
return [
|
||||
// this SDP is used by the intercom and will send the encrypted packets which we don't care about to the loopback on port 65000 of the intercom
|
||||
`m=audio 65000 RTP/SAVP 110`,
|
||||
`a=rtpmap:110 speex/8000`,
|
||||
`a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`,
|
||||
]
|
||||
}, ( video ) => {
|
||||
return [
|
||||
// this SDP is used by the intercom and will send the encrypted packets which we don't care about to the loopback on port 65000 of the intercom
|
||||
`m=video 65002 RTP/SAVP 96`,
|
||||
`a=rtpmap:96 H264/90000`,
|
||||
`a=fmtp:96 profile-level-id=42801F`,
|
||||
`a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`,
|
||||
'a=recvonly'
|
||||
]
|
||||
}, this.incomingCallRequest );
|
||||
|
||||
this.incomingCallRequest = undefined
|
||||
|
||||
return sip
|
||||
}
|
||||
|
||||
getSipMediaStreamOptions(): ResponseMediaStreamOptions {
|
||||
@@ -362,13 +517,16 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid
|
||||
name: 'SIP',
|
||||
// this stream is NOT scrypted blessed due to wackiness in the h264 stream.
|
||||
// tool: "scrypted",
|
||||
container: 'sdp',
|
||||
container: 'rtsp',
|
||||
video: {
|
||||
codec: 'h264'
|
||||
},
|
||||
audio: {
|
||||
// this is a hint to let homekit, et al, know that it's speex audio and needs transcoding.
|
||||
codec: 'speex',
|
||||
},
|
||||
source: 'cloud', // to disable prebuffering
|
||||
userConfigurable: false,
|
||||
userConfigurable: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -378,14 +536,28 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid
|
||||
]
|
||||
}
|
||||
|
||||
async getDevice(nativeId: string) : Promise<BticinoSipLock> {
|
||||
async getDevice(nativeId: string) : Promise<any> {
|
||||
if( nativeId && nativeId.endsWith('-aswm-switch')) {
|
||||
this.aswmSwitch = new BticinoAswmSwitch(this, this.voicemailHandler)
|
||||
return this.aswmSwitch
|
||||
} else if( nativeId && nativeId.endsWith('-mute-switch') ) {
|
||||
this.muteSwitch = new BticinoMuteSwitch(this)
|
||||
return this.muteSwitch
|
||||
}
|
||||
return new BticinoSipLock(this)
|
||||
}
|
||||
|
||||
async releaseDevice(id: string, nativeId: string): Promise<void> {
|
||||
this.voicemailHandler.cancelTimer()
|
||||
this.persistentSipManager.cancelTimer()
|
||||
this.controllerApi.cancelTimer()
|
||||
if( nativeId?.endsWith('-aswm-switch') ) {
|
||||
this.aswmSwitch.cancelTimer()
|
||||
} else if( nativeId?.endsWith('mute-switch') ) {
|
||||
this.muteSwitch.cancelTimer()
|
||||
} else {
|
||||
this.stopIntercom()
|
||||
this.voicemailHandler.cancelTimer()
|
||||
this.persistentSipManager.cancelTimer()
|
||||
this.controllerApi.cancelTimer()
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
|
||||
64
plugins/bticino/src/bticino-mute-switch.ts
Normal file
64
plugins/bticino/src/bticino-mute-switch.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { ScryptedDeviceBase, HttpRequest, HttpResponse, HttpRequestHandler, OnOff } from "@scrypted/sdk";
|
||||
import { BticinoSipCamera } from "./bticino-camera";
|
||||
|
||||
export class BticinoMuteSwitch extends ScryptedDeviceBase implements OnOff, HttpRequestHandler {
|
||||
private timeout : NodeJS.Timeout
|
||||
|
||||
constructor(private camera: BticinoSipCamera) {
|
||||
super( camera.nativeId + "-mute-switch");
|
||||
this.on = false;
|
||||
this.timeout = setTimeout( () => this.syncStatus() , 5000 )
|
||||
}
|
||||
|
||||
turnOff(): Promise<void> {
|
||||
this.on = false
|
||||
return this.camera.muteRinger(false)
|
||||
}
|
||||
|
||||
turnOn(): Promise<void> {
|
||||
this.on = true
|
||||
return this.camera.muteRinger(true)
|
||||
}
|
||||
|
||||
syncStatus() {
|
||||
this.camera.muteStatus().then( (value) => {
|
||||
this.on = value["status"]
|
||||
} ).catch( (e) => { this.camera.console.error(e) } ).finally( () => {
|
||||
this.timeout = setTimeout( () => this.syncStatus() , 60000 )
|
||||
} )
|
||||
}
|
||||
|
||||
cancelTimer() {
|
||||
if( this.timeout ) {
|
||||
clearTimeout(this.timeout)
|
||||
}
|
||||
}
|
||||
|
||||
public async onRequest(request: HttpRequest, response: HttpResponse): Promise<void> {
|
||||
if (request.url.endsWith('/disabled')) {
|
||||
this.on = false
|
||||
response.send('Success', {
|
||||
code: 200,
|
||||
});
|
||||
} else if( request.url.endsWith('/enabled') ) {
|
||||
this.on = true
|
||||
response.send('Success', {
|
||||
code: 200,
|
||||
});
|
||||
} else if( request.url.endsWith('/enable') ) {
|
||||
this.turnOn()
|
||||
response.send('Success', {
|
||||
code: 200,
|
||||
});
|
||||
} else if( request.url.endsWith('/disable') ) {
|
||||
this.turnOff()
|
||||
response.send('Success', {
|
||||
code: 200,
|
||||
});
|
||||
} else {
|
||||
response.send('Unsupported operation', {
|
||||
code: 400,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { BticinoSipCamera } from "./bticino-camera"
|
||||
|
||||
export class VoicemailHandler extends SipRequestHandler {
|
||||
private timeout : NodeJS.Timeout
|
||||
private aswmIsEnabled: boolean
|
||||
|
||||
constructor( private sipCamera : BticinoSipCamera ) {
|
||||
super()
|
||||
@@ -15,14 +16,12 @@ export class VoicemailHandler extends SipRequestHandler {
|
||||
checkVoicemail() {
|
||||
if( !this.sipCamera )
|
||||
return
|
||||
if( this.isEnabled() ) {
|
||||
this.sipCamera.console.debug("Checking answering machine, cameraId: " + this.sipCamera.id )
|
||||
this.sipCamera.getAswmStatus().catch( e => this.sipCamera.console.error(e) )
|
||||
} else {
|
||||
this.sipCamera.console.debug("Answering machine check not enabled, cameraId: " + this.sipCamera.id )
|
||||
}
|
||||
//TODO: make interval customizable, now every 5 minutes
|
||||
this.timeout = setTimeout( () => this.checkVoicemail() , 5 * 60 * 1000 )
|
||||
|
||||
this.sipCamera.console.debug("Checking answering machine, cameraId: " + this.sipCamera.id )
|
||||
this.sipCamera.getAswmStatus().catch( e => this.sipCamera.console.error(e) )
|
||||
|
||||
//TODO: make interval customizable, now every minute
|
||||
this.timeout = setTimeout( () => this.checkVoicemail() , 1 * 60 * 1000 )
|
||||
}
|
||||
|
||||
cancelTimer() {
|
||||
@@ -32,10 +31,11 @@ export class VoicemailHandler extends SipRequestHandler {
|
||||
}
|
||||
|
||||
handle(request: SipRequest) {
|
||||
if( this.isEnabled() ) {
|
||||
const lastVoicemailMessageTimestamp : number = Number.parseInt( this.sipCamera.storage.getItem('lastVoicemailMessageTimestamp') ) || -1
|
||||
const message : string = request.content.toString()
|
||||
if( message.startsWith('*#8**40*0*0*1176*0*2##') ) {
|
||||
const lastVoicemailMessageTimestamp : number = Number.parseInt( this.sipCamera.storage.getItem('lastVoicemailMessageTimestamp') ) || -1
|
||||
const message : string = request.content.toString()
|
||||
if( message.startsWith('*#8**40*0*0*') || message.startsWith('*#8**40*1*0*') ) {
|
||||
this.aswmIsEnabled = message.startsWith('*#8**40*1*0*');
|
||||
if( this.isEnabled() ) {
|
||||
this.sipCamera.console.debug("Handling incoming answering machine reply")
|
||||
const messages : string[] = message.split(';')
|
||||
let lastMessageTimestamp : number = 0
|
||||
@@ -53,12 +53,12 @@ export class VoicemailHandler extends SipRequestHandler {
|
||||
}
|
||||
} )
|
||||
if( (lastVoicemailMessageTimestamp == null && lastMessageTimestamp > 0) ||
|
||||
( lastVoicemailMessageTimestamp != null && lastMessageTimestamp > lastVoicemailMessageTimestamp ) ) {
|
||||
( lastVoicemailMessageTimestamp != null && lastMessageTimestamp > lastVoicemailMessageTimestamp ) ) {
|
||||
this.sipCamera.log.a(`You have ${countNewMessages} new voicemail messages.`)
|
||||
this.sipCamera.storage.setItem('lastVoicemailMessageTimestamp', lastMessageTimestamp.toString())
|
||||
} else {
|
||||
} else {
|
||||
this.sipCamera.console.debug("No new messages since: " + lastVoicemailMessageTimestamp + " lastMessage: " + lastMessageTimestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,4 +66,8 @@ export class VoicemailHandler extends SipRequestHandler {
|
||||
isEnabled() : boolean {
|
||||
return this.sipCamera?.storage?.getItem('notifyVoicemail')?.toLocaleLowerCase() === 'true' || false
|
||||
}
|
||||
|
||||
isAswmEnabled() : boolean {
|
||||
return this.aswmIsEnabled
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ export class ControllerApi {
|
||||
})
|
||||
}
|
||||
console.log("Endpoint registration status: " + res.statusCode)
|
||||
});
|
||||
}).on('error', (e) => this.sipCamera.console.error(e) );
|
||||
|
||||
// The default evict time on the c300x-controller is 5 minutes, so this will certainly be within bounds
|
||||
this.timeout = setTimeout( () => this.registerEndpoints( false ) , 2 * 60 * 1000 )
|
||||
@@ -114,7 +114,7 @@ export class ControllerApi {
|
||||
return new Promise( (resolve, reject) => get(`http://${ipAddress}:8080/register-endpoint?raw=true&updateStreamEndpoint=${sipFrom}`, (res) => {
|
||||
if( res.statusCode != 200 ) reject( "ERROR: Could not update streaming endpoint, call returned: " + res.statusCode )
|
||||
else resolve()
|
||||
} ) );
|
||||
} ).on('error', (error) => this.sipCamera.console.error(error) ).end() );
|
||||
}
|
||||
|
||||
public cancelTimer() {
|
||||
|
||||
@@ -36,7 +36,7 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid
|
||||
const name = settings.newCamera?.toString() === undefined ? "Doorbell" : settings.newCamera?.toString()
|
||||
await this.updateDevice(nativeId, name)
|
||||
|
||||
const device: Device = {
|
||||
const lockDevice: Device = {
|
||||
providerNativeId: nativeId,
|
||||
info: {
|
||||
//model: `${camera.model} (${camera.data.kind})`,
|
||||
@@ -49,10 +49,38 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid
|
||||
type: ScryptedDeviceType.Lock,
|
||||
interfaces: [ScryptedInterface.Lock, ScryptedInterface.HttpRequestHandler],
|
||||
}
|
||||
|
||||
const aswmSwitchDevice: Device = {
|
||||
providerNativeId: nativeId,
|
||||
info: {
|
||||
//model: `${camera.model} (${camera.data.kind})`,
|
||||
manufacturer: 'BticinoPlugin',
|
||||
//firmware: camera.data.firmware_version,
|
||||
//serialNumber: camera.data.device_id
|
||||
},
|
||||
nativeId: nativeId + '-aswm-switch',
|
||||
name: name + ' Voicemail',
|
||||
type: ScryptedDeviceType.Switch,
|
||||
interfaces: [ScryptedInterface.OnOff, ScryptedInterface.HttpRequestHandler],
|
||||
}
|
||||
|
||||
const muteSwitchDevice: Device = {
|
||||
providerNativeId: nativeId,
|
||||
info: {
|
||||
//model: `${camera.model} (${camera.data.kind})`,
|
||||
manufacturer: 'BticinoPlugin',
|
||||
//firmware: camera.data.firmware_version,
|
||||
//serialNumber: camera.data.device_id
|
||||
},
|
||||
nativeId: nativeId + '-mute-switch',
|
||||
name: name + ' Muted',
|
||||
type: ScryptedDeviceType.Switch,
|
||||
interfaces: [ScryptedInterface.OnOff, ScryptedInterface.HttpRequestHandler],
|
||||
}
|
||||
|
||||
await deviceManager.onDevicesChanged({
|
||||
providerNativeId: nativeId,
|
||||
devices: [device],
|
||||
devices: [lockDevice, aswmSwitchDevice, muteSwitchDevice],
|
||||
})
|
||||
|
||||
let sipCamera : BticinoSipCamera = await this.getDevice(nativeId)
|
||||
@@ -87,6 +115,7 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid
|
||||
ScryptedInterface.Settings,
|
||||
ScryptedInterface.Intercom,
|
||||
ScryptedInterface.BinarySensor,
|
||||
ScryptedInterface.MotionSensor,
|
||||
ScryptedDeviceType.DeviceProvider,
|
||||
ScryptedInterface.HttpRequestHandler,
|
||||
ScryptedInterface.VideoClips,
|
||||
|
||||
@@ -61,7 +61,7 @@ export class SipHelper {
|
||||
if( !md5 ) {
|
||||
md5 = crypto.createHash('md5').update( camera.nativeId ).digest("hex")
|
||||
md5 = md5.substring(0, 8) + '-' + md5.substring(8, 12) + '-' + md5.substring(12,16) + '-' + md5.substring(16, 32)
|
||||
camera.storage.setItem('md5has', md5)
|
||||
camera.storage.setItem('md5hash', md5)
|
||||
}
|
||||
return md5
|
||||
}
|
||||
|
||||
@@ -35,6 +35,14 @@ export class BticinoStorageSettings {
|
||||
defaultValue: 600,
|
||||
placeholder: '600',
|
||||
},
|
||||
thumbnailCacheTime: {
|
||||
title: 'Thumbnail cache time',
|
||||
type: 'number',
|
||||
range: [60, 86400],
|
||||
description: 'How long the snapshot is cached before taking a new one. (in seconds)',
|
||||
defaultValue: 300,
|
||||
placeholder: '300',
|
||||
},
|
||||
sipdebug: {
|
||||
title: 'SIP debug logging',
|
||||
type: 'boolean',
|
||||
|
||||
@@ -6,11 +6,39 @@
|
||||
See below for additional recommendations.
|
||||
|
||||
## Port Forwarding
|
||||
**Important Note**: Ports 10443 and 10444 are already being used by Scrypted itself. So, please choose a different port number, like 11443.
|
||||
|
||||
1. Open the Firewall and Port Forwarding Settings on the network's router.
|
||||
2. Use the ports shown in Settings to configure a Port Forwarding rule on the router.
|
||||
### What You'll Need
|
||||
- Access to your router's settings (usually through a web browser).
|
||||
- Ability to change settings on your host machine's firewall (like ufw for Linux or Windows Firewall for Windows).
|
||||
|
||||
Use the `Test Port Forward` buttin in `Advanced` Settings tab to verify the configuration is correct.
|
||||
### Step-by-Step Instructions
|
||||
|
||||
1. **Port Configuration**
|
||||
- For simplicity, use the same port number (e.g 11443) for both "From Port" and "Forward Port" fields in the Scrypted Cloud plugin settings General tab.
|
||||
|
||||
2. **Access Your Router Settings**
|
||||
- Open your web browser and go to your router's login page. You may need the router's IP address, username, and password.
|
||||
> If you're not sure how to do this, [find the guide specific to your router here](https://portforward.com/router.htm).
|
||||
|
||||
3. **Navigate to Firewall or Port Forwarding Section**
|
||||
- Once logged in, find the section that deals with "Firewall" or "Port Forwarding". It could be under tabs like "Advanced," "NAT," or "Security."
|
||||
|
||||
4. **Set Up Port Forwarding Rule**
|
||||
- Use the port number you chose in Step 1 (e.g 11443) to set up a new Port Forwarding rule on your router.
|
||||
|
||||
5. **Change Port Forwarding Mode in Scrypted**
|
||||
- Go back to Scrypted and navigate to the "General" tab in the Cloud plugin.
|
||||
- Select "Router Forward" from the "Port Forwarding Mode" dropdown menu.
|
||||
|
||||
6. **Test Your Setup**
|
||||
- In the Scrypted Cloud plugin settings, find and click the `Test Port Forward` button under the `Advanced` Settings tab. This will confirm if you've set everything up correctly.
|
||||
|
||||
7. **Save Your Settings**
|
||||
- Don't forget to save your changes in both your router and in Scrypted.
|
||||
|
||||
### Firewall Configuration
|
||||
Make sure your host machine’s firewall isn't blocking the port you've chosen. You may need to create an 'allow' rule for this port in your host's firewall settings.
|
||||
|
||||
## Custom Domains
|
||||
|
||||
@@ -26,7 +54,7 @@ Scrypted Cloud automatically creates a login free tunnel for remote access.
|
||||
The following steps are only necessary if you want to associate the tunnel with your existing Cloudflare account to manage it remotely.
|
||||
|
||||
1. Create the Tunnel in the [Cloudflare Zero Trust Dashboard](https://one.dash.cloudflare.com).
|
||||
2. Copy the token shown for the tunnel shown in the `install [token]` command. E.g. `cloudflared service install eyJhI344aA...`.
|
||||
2. Copy the token shown for the tunnel shown in the `install [token]` command. For example, if you see `cloudflared service install eyJhI344aA...`, then `eyJhI344aA...` is the token you need to copy.
|
||||
3. Paste the token into the Cloud Plugin Advanced Settings.
|
||||
4. Add a `Public Hostname` to the tunnel.
|
||||
* Choose a (sub)domain.
|
||||
@@ -34,4 +62,4 @@ The following steps are only necessary if you want to associate the tunnel with
|
||||
* Expand `Additional Application Settings` -> `TLS` menus and enable `No TLS Verify`.
|
||||
|
||||
5. Reload Cloud Plugin.
|
||||
6. Verify Cloudflare successfully connected by observing the `Console` Logs.
|
||||
6. Verify Cloudflare successfully connected by observing the `Console` Logs.
|
||||
|
||||
4
plugins/cloud/package-lock.json
generated
4
plugins/cloud/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/cloud",
|
||||
"version": "0.2.3",
|
||||
"version": "0.2.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/cloud",
|
||||
"version": "0.2.3",
|
||||
"version": "0.2.4",
|
||||
"dependencies": {
|
||||
"@eneris/push-receiver": "^3.1.4",
|
||||
"@scrypted/common": "file:../../common",
|
||||
|
||||
@@ -54,5 +54,5 @@
|
||||
"@types/nat-upnp": "^1.1.2",
|
||||
"@types/node": "^20.4.5"
|
||||
},
|
||||
"version": "0.2.3"
|
||||
"version": "0.2.4"
|
||||
}
|
||||
|
||||
@@ -574,6 +574,8 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
});
|
||||
|
||||
const { token_info } = this.storageSettings.values;
|
||||
if (!token_info)
|
||||
throw new Error('Scrypted Cloud is not logged in. Skipping home.scrypted.app registration.');
|
||||
const response = await axios(`https://${SCRYPTED_SERVER}/_punch/register?${q}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token_info}`
|
||||
|
||||
4
plugins/core/.vscode/launch.json
vendored
4
plugins/core/.vscode/launch.json
vendored
@@ -10,14 +10,14 @@
|
||||
"port": 10081,
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"**/plugin-remote-worker.*",
|
||||
"**/plugin-console.*",
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"preLaunchTask": "scrypted: deploy+debug",
|
||||
"sourceMaps": true,
|
||||
"localRoot": "${workspaceFolder}/out",
|
||||
"remoteRoot": "/plugin/",
|
||||
"type": "pwa-node"
|
||||
"type": "node"
|
||||
}
|
||||
]
|
||||
}
|
||||
1064
plugins/core/package-lock.json
generated
1064
plugins/core/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/core",
|
||||
"version": "0.1.143",
|
||||
"version": "0.3.1",
|
||||
"description": "Scrypted Core plugin. Provides the UI, websocket, and engine.io APIs.",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache-2.0",
|
||||
@@ -42,11 +42,12 @@
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"mime": "^3.0.0",
|
||||
"router": "^1.3.6",
|
||||
"typescript": "^4.5.5"
|
||||
"node-pty-prebuilt-multiarch": "^0.10.1-pre.5",
|
||||
"router": "^1.3.8",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mime": "^2.0.3",
|
||||
"@types/node": "^16.9.0"
|
||||
"@types/mime": "^3.0.4",
|
||||
"@types/node": "^20.9.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export class Timer {
|
||||
|
||||
}
|
||||
@@ -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';
|
||||
@@ -9,13 +9,12 @@ import { AggregateCore, AggregateCoreNativeId } from './aggregate-core';
|
||||
import { AutomationCore, AutomationCoreNativeId } from './automations-core';
|
||||
import { LauncherMixin } from './launcher-mixin';
|
||||
import { MediaCore } from './media-core';
|
||||
import { ScriptCore, ScriptCoreNativeId } from './script-core';
|
||||
import { newScript, ScriptCore, ScriptCoreNativeId } from './script-core';
|
||||
import { TerminalService, TerminalServiceNativeId } from './terminal-service';
|
||||
import { UsersCore, UsersNativeId } from './user';
|
||||
|
||||
const { systemManager, deviceManager, endpointManager } = sdk;
|
||||
|
||||
const indexHtml = fs.readFileSync('dist/index.html').toString();
|
||||
|
||||
export function getAddresses() {
|
||||
const addresses: string[] = [];
|
||||
for (const [iface, nif] of Object.entries(os.networkInterfaces())) {
|
||||
@@ -39,6 +38,7 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Eng
|
||||
aggregateCore: AggregateCore;
|
||||
automationCore: AutomationCore;
|
||||
users: UsersCore;
|
||||
terminalService: TerminalService;
|
||||
localAddresses: string[];
|
||||
storageSettings = new StorageSettings(this, {
|
||||
localAddresses: {
|
||||
@@ -59,10 +59,13 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Eng
|
||||
},
|
||||
}
|
||||
});
|
||||
indexHtml: string;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.indexHtml = readFileAsString('dist/index.html');
|
||||
|
||||
(async () => {
|
||||
await deviceManager.onDeviceDiscovered(
|
||||
{
|
||||
@@ -83,6 +86,16 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Eng
|
||||
},
|
||||
);
|
||||
})();
|
||||
(async () => {
|
||||
await deviceManager.onDeviceDiscovered(
|
||||
{
|
||||
name: 'Terminal Service',
|
||||
nativeId: TerminalServiceNativeId,
|
||||
interfaces: [ScryptedInterface.StreamService],
|
||||
type: ScryptedDeviceType.Builtin,
|
||||
},
|
||||
);
|
||||
})();
|
||||
|
||||
(async () => {
|
||||
await deviceManager.onDeviceDiscovered(
|
||||
@@ -157,6 +170,8 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Eng
|
||||
return this.aggregateCore ||= new AggregateCore();
|
||||
if (nativeId === UsersNativeId)
|
||||
return this.users ||= new UsersCore();
|
||||
if (nativeId === TerminalServiceNativeId)
|
||||
return this.terminalService ||= new TerminalService();
|
||||
}
|
||||
|
||||
async releaseDevice(id: string, nativeId: string): Promise<void> {
|
||||
@@ -198,7 +213,7 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Eng
|
||||
ws.close();
|
||||
}
|
||||
|
||||
handlePublicFinal(request: HttpRequest, response: HttpResponse) {
|
||||
async handlePublicFinal(request: HttpRequest, response: HttpResponse) {
|
||||
// need to strip off the query.
|
||||
const incomingPathname = request.url.split('?')[0];
|
||||
if (request.url !== '/index.html') {
|
||||
@@ -208,24 +223,24 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Eng
|
||||
|
||||
// the rel hrefs (manifest, icons) are pulled in a web worker which does not
|
||||
// have cookies. need to attach auth info to them.
|
||||
endpointManager.getPublicCloudEndpoint()
|
||||
.then(endpoint => {
|
||||
const u = new URL(endpoint);
|
||||
try {
|
||||
const endpoint = await endpointManager.getPublicCloudEndpoint();
|
||||
const u = new URL(endpoint);
|
||||
|
||||
const rewritten = indexHtml
|
||||
.replace('href="manifest.json"', `href="manifest.json${u.search}"`)
|
||||
.replace('href="img/icons/apple-touch-icon-152x152.png"', `href="img/icons/apple-touch-icon-152x152.png${u.search}"`)
|
||||
.replace('href="img/icons/safari-pinned-tab.svg"', `href="img/icons/safari-pinned-tab.svg${u.search}"`)
|
||||
;
|
||||
response.send(rewritten, {
|
||||
headers: {
|
||||
'Content-Type': 'text/html',
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
response.sendFile("dist" + incomingPathname);
|
||||
const rewritten = this.indexHtml
|
||||
.replace('href="manifest.json"', `href="manifest.json${u.search}"`)
|
||||
.replace('href="img/icons/apple-touch-icon-152x152.png"', `href="img/icons/apple-touch-icon-152x152.png${u.search}"`)
|
||||
.replace('href="img/icons/safari-pinned-tab.svg"', `href="img/icons/safari-pinned-tab.svg${u.search}"`)
|
||||
;
|
||||
response.send(rewritten, {
|
||||
headers: {
|
||||
'Content-Type': 'text/html',
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
response.sendFile("dist" + incomingPathname);
|
||||
}
|
||||
}
|
||||
|
||||
async onRequest(request: HttpRequest, response: HttpResponse) {
|
||||
@@ -238,13 +253,13 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Eng
|
||||
}
|
||||
|
||||
if (request.isPublicEndpoint) {
|
||||
this.publicRouter(normalizedRequest, response, () => this.handlePublicFinal(normalizedRequest, response));
|
||||
await new Promise(resolve => this.publicRouter(normalizedRequest, response, resolve));
|
||||
await this.handlePublicFinal(normalizedRequest, response);
|
||||
}
|
||||
else {
|
||||
this.router(normalizedRequest, response, () => {
|
||||
response.send('Not Found', {
|
||||
code: 404,
|
||||
});
|
||||
await new Promise(resolve => this.router(normalizedRequest, response, resolve));
|
||||
response.send('Not Found', {
|
||||
code: 404,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -255,5 +270,6 @@ export default ScryptedCore;
|
||||
export async function fork() {
|
||||
return {
|
||||
tsCompile,
|
||||
newScript,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export class MediaCore extends ScryptedDeviceBase implements DeviceProvider, Buf
|
||||
constructor() {
|
||||
super(MediaCoreNativeId);
|
||||
|
||||
this.fromMimeType = ScryptedMimeTypes.SchemePrefix + 'scrypted-media';
|
||||
this.fromMimeType = ScryptedMimeTypes.SchemePrefix + 'scrypted-media' + ';converter-weight=2';
|
||||
this.toMimeType = ScryptedMimeTypes.MediaObject;
|
||||
|
||||
(async () => {
|
||||
|
||||
@@ -1,33 +1,23 @@
|
||||
import { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, Readme, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, Setting } from "@scrypted/sdk";
|
||||
import { Script } from "./script";
|
||||
import sdk from '@scrypted/sdk';
|
||||
import sdk, { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, Readme, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedNativeId, Setting } from '@scrypted/sdk';
|
||||
import { randomBytes } from "crypto";
|
||||
import fs from 'fs';
|
||||
import path from "path/posix";
|
||||
import { Worker } from "worker_threads";
|
||||
import { Script } from "./script";
|
||||
|
||||
const { deviceManager } = sdk;
|
||||
export const ScriptCoreNativeId = 'scriptcore';
|
||||
|
||||
interface ScriptWorker {
|
||||
script: Script;
|
||||
worker: Worker;
|
||||
}
|
||||
|
||||
export class ScriptCore extends ScryptedDeviceBase implements DeviceProvider, DeviceCreator, Readme {
|
||||
scripts = new Map<string, Promise<Script>>();
|
||||
scripts = new Map<string, ScriptWorker>();
|
||||
|
||||
constructor() {
|
||||
super(ScriptCoreNativeId);
|
||||
|
||||
for (const nativeId of deviceManager.getNativeIds()) {
|
||||
if (nativeId?.startsWith('script:')) {
|
||||
const script = new Script(nativeId);
|
||||
this.scripts.set(nativeId, (async () => {
|
||||
if (script.providedInterfaces.length > 2) {
|
||||
await script.run();
|
||||
}
|
||||
else {
|
||||
this.reportScript(nativeId);
|
||||
}
|
||||
return script;
|
||||
})());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getCreateDeviceSettings(): Promise<Setting[]> {
|
||||
@@ -51,6 +41,10 @@ export class ScriptCore extends ScryptedDeviceBase implements DeviceProvider, De
|
||||
const nativeId = 'script:' + randomBytes(8).toString('hex');
|
||||
await this.reportScript(nativeId, name?.toString());
|
||||
const script = new Script(nativeId);
|
||||
this.scripts.set(nativeId, {
|
||||
script,
|
||||
worker: undefined,
|
||||
});
|
||||
if (template) {
|
||||
try {
|
||||
await script.saveScript({
|
||||
@@ -65,7 +59,6 @@ export class ScriptCore extends ScryptedDeviceBase implements DeviceProvider, De
|
||||
catch (e) {
|
||||
}
|
||||
}
|
||||
this.scripts.set(nativeId, Promise.resolve(script));
|
||||
return nativeId;
|
||||
}
|
||||
|
||||
@@ -84,10 +77,75 @@ export class ScriptCore extends ScryptedDeviceBase implements DeviceProvider, De
|
||||
return await deviceManager.onDeviceDiscovered(device);
|
||||
}
|
||||
|
||||
getDevice(nativeId: string) {
|
||||
return this.scripts.get(nativeId);
|
||||
async getDevice(nativeId: string) {
|
||||
const e = this.scripts.get(nativeId);
|
||||
if (e) {
|
||||
if (e.script)
|
||||
return e.script;
|
||||
e.worker?.terminate();
|
||||
this.scripts.delete(nativeId);
|
||||
}
|
||||
|
||||
let script = new Script(nativeId);
|
||||
let worker: Worker;
|
||||
|
||||
const triggerDeviceDiscover = async (name: string, type: ScryptedDeviceType, interfaces: string[]) => {
|
||||
const e = this.scripts.get(nativeId);
|
||||
if (e?.script == script)
|
||||
e.script = undefined;
|
||||
|
||||
const device: Device = {
|
||||
providerNativeId: this.nativeId,
|
||||
name,
|
||||
nativeId,
|
||||
type,
|
||||
interfaces,
|
||||
refresh: true,
|
||||
};
|
||||
return await deviceManager.onDeviceDiscovered(device);
|
||||
};
|
||||
|
||||
if (script.providedInterfaces.length > 2) {
|
||||
const fork = sdk.fork<{
|
||||
newScript: typeof newScript,
|
||||
}>();
|
||||
worker = fork.worker;
|
||||
try {
|
||||
script = await (await fork.result).newScript(nativeId, triggerDeviceDiscover);
|
||||
}
|
||||
catch (e) {
|
||||
worker.terminate();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
worker?.on('exit', () => {
|
||||
if (this.scripts.get(nativeId)?.worker === worker) {
|
||||
this.scripts.delete(nativeId);
|
||||
// notify the system that the device needs to be refreshed.
|
||||
if (deviceManager.getNativeIds().includes(nativeId)) {
|
||||
const script = new Script(nativeId);
|
||||
triggerDeviceDiscover(script.providedName, script.providedType, script.providedInterfaces);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.scripts.set(nativeId, {
|
||||
script,
|
||||
worker,
|
||||
});
|
||||
return script;
|
||||
}
|
||||
|
||||
async releaseDevice(id: string, nativeId: string): Promise<void> {
|
||||
const worker = this.scripts.get(nativeId)?.worker;
|
||||
this.scripts.delete(nativeId);
|
||||
worker?.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
export async function newScript(nativeId: ScryptedNativeId, triggerDeviceDiscover: (name: string, type: ScryptedDeviceType, interfaces: string[]) => Promise<string>) {
|
||||
const script = new Script(nativeId, triggerDeviceDiscover);
|
||||
await script.run();
|
||||
return script;
|
||||
}
|
||||
|
||||
@@ -3,14 +3,11 @@ 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 { log, deviceManager, systemManager } = sdk;
|
||||
const { deviceManager } = sdk;
|
||||
|
||||
export class Script extends ScryptedDeviceBase implements Scriptable, Program, ScriptDeviceImpl {
|
||||
apiProxy: PluginAPIProxy;
|
||||
|
||||
constructor(nativeId: string) {
|
||||
constructor(nativeId: string, public triggerDeviceDiscover?: (name: string, type: ScryptedDeviceType, interfaces: string[]) => Promise<string>) {
|
||||
super(nativeId);
|
||||
}
|
||||
|
||||
@@ -18,6 +15,8 @@ export class Script extends ScryptedDeviceBase implements Scriptable, Program, S
|
||||
this.storage.setItem('data', JSON.stringify({
|
||||
'script.ts': source.script,
|
||||
}));
|
||||
|
||||
this.triggerDeviceDiscover?.(this.providedName, this.providedType, this.providedInterfaces);
|
||||
}
|
||||
|
||||
async loadScripts(): Promise<{ [filename: string]: ScriptSource; }> {
|
||||
@@ -70,46 +69,38 @@ export class Script extends ScryptedDeviceBase implements Scriptable, Program, S
|
||||
}
|
||||
|
||||
prepareScript() {
|
||||
this.apiProxy?.removeListeners();
|
||||
|
||||
Object.assign(this, createScriptDevice([
|
||||
ScryptedInterface.Scriptable,
|
||||
ScryptedInterface.Program,
|
||||
]));
|
||||
}
|
||||
|
||||
async run(variables?: { [name: string]: any; }): Promise<any> {
|
||||
async runInternal(script: string, variables?: { [name: string]: any; }): Promise<any> {
|
||||
this.prepareScript();
|
||||
|
||||
try {
|
||||
const data = JSON.parse(this.storage.getItem('data'));
|
||||
|
||||
const { value, defaultExport, apiProxy } = await scryptedEval(this, data['script.ts'], Object.assign({
|
||||
const { value, defaultExport } = await scryptedEval(this, script, Object.assign({
|
||||
device: this,
|
||||
}, variables));
|
||||
|
||||
this.apiProxy = apiProxy;
|
||||
|
||||
await this.postRunScript(defaultExport);
|
||||
return value;
|
||||
}
|
||||
catch (e) {
|
||||
this.console.error('error loading script', e);
|
||||
this.console.error('error evaluating script', e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async run(variables?: { [name: string]: any; }): Promise<any> {
|
||||
const data = JSON.parse(this.storage.getItem('data'));
|
||||
return this.runInternal(data['script.ts'], variables)
|
||||
}
|
||||
|
||||
async eval(source: ScriptSource, variables?: { [name: string]: any }) {
|
||||
this.prepareScript();
|
||||
|
||||
const { value, defaultExport, apiProxy } = await scryptedEval(this, source.script, Object.assign({
|
||||
device: this,
|
||||
}, variables));
|
||||
|
||||
this.apiProxy = apiProxy;
|
||||
|
||||
await this.postRunScript(defaultExport);
|
||||
return value;
|
||||
return this.runInternal(source.script, variables);
|
||||
}
|
||||
|
||||
// will be done at runtime
|
||||
|
||||
202
plugins/core/src/terminal-service.ts
Normal file
202
plugins/core/src/terminal-service.ts
Normal file
@@ -0,0 +1,202 @@
|
||||
import { ScryptedDeviceBase, ScryptedNativeId, StreamService } from "@scrypted/sdk";
|
||||
import type { IPty, spawn as ptySpawn } from 'node-pty-prebuilt-multiarch';
|
||||
import { createAsyncQueue } from '@scrypted/common/src/async-queue'
|
||||
import { ChildProcess, spawn as childSpawn } from "child_process";
|
||||
|
||||
export const TerminalServiceNativeId = 'terminalservice';
|
||||
|
||||
|
||||
class InteractiveTerminal {
|
||||
cp: IPty
|
||||
|
||||
constructor(cmd: string[], spawn: typeof ptySpawn) {
|
||||
if (cmd?.length) {
|
||||
this.cp = spawn(cmd[0], cmd.slice(1), {});
|
||||
} else {
|
||||
this.cp = spawn(process.env.SHELL as string, [], {});
|
||||
}
|
||||
}
|
||||
|
||||
onExit(fn: (e: { exitCode: number; signal?: number; }) => any) {
|
||||
this.cp.onExit(fn)
|
||||
};
|
||||
|
||||
onData(fn: (e: string) => any) {
|
||||
this.cp.onData(fn);
|
||||
}
|
||||
|
||||
pause() {
|
||||
this.cp.pause();
|
||||
}
|
||||
|
||||
resume() {
|
||||
this.cp.resume();
|
||||
}
|
||||
|
||||
write(data: Buffer) {
|
||||
this.cp.write(data.toString());
|
||||
}
|
||||
|
||||
sendEOF() {
|
||||
// not supported
|
||||
}
|
||||
|
||||
kill(signal?: string) {
|
||||
this.cp.kill(signal);
|
||||
}
|
||||
|
||||
resize(columns: number, rows: number) {
|
||||
if (columns > 0 && rows > 0)
|
||||
this.cp.resize(columns, rows);
|
||||
}
|
||||
}
|
||||
|
||||
class NoninteractiveTerminal {
|
||||
cp: ChildProcess
|
||||
|
||||
constructor(cmd: string[]) {
|
||||
if (cmd?.length) {
|
||||
this.cp = childSpawn(cmd[0], cmd.slice(1));
|
||||
} else {
|
||||
this.cp = childSpawn(process.env.SHELL as string);
|
||||
}
|
||||
}
|
||||
|
||||
onExit(fn: (code: number, signal: NodeJS.Signals) => void) {
|
||||
return this.cp.on("close", fn);
|
||||
}
|
||||
|
||||
onData(fn: { (chunk: any): void; (chunk: any): void; }) {
|
||||
this.cp.stdout.on("data", fn);
|
||||
this.cp.stderr.on("data", fn);
|
||||
}
|
||||
|
||||
pause() {
|
||||
this.cp.stdout.pause();
|
||||
this.cp.stderr.pause();
|
||||
}
|
||||
|
||||
resume() {
|
||||
this.cp.stdout.resume();
|
||||
this.cp.stderr.resume();
|
||||
}
|
||||
|
||||
write(data: Buffer) {
|
||||
this.cp.stdin.write(data);
|
||||
}
|
||||
|
||||
sendEOF() {
|
||||
this.cp.stdin.end();
|
||||
}
|
||||
|
||||
kill(signal?: number | NodeJS.Signals) {
|
||||
this.cp.kill(signal);
|
||||
}
|
||||
|
||||
resize(columns: number, rows: number) {
|
||||
// not supported
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class TerminalService extends ScryptedDeviceBase implements StreamService {
|
||||
constructor(nativeId?: ScryptedNativeId) {
|
||||
super(TerminalServiceNativeId);
|
||||
}
|
||||
|
||||
/*
|
||||
* The input to this stream can send buffers for normal terminal data and strings
|
||||
* for control messages. Control messages are JSON-formatted.
|
||||
*
|
||||
* The current implemented control messages:
|
||||
*
|
||||
* Start: { "interactive": boolean, "cmd": string[] }
|
||||
* Resize: { "dim": { "cols": number, "rows": number } }
|
||||
* EOF: { "eof": true }
|
||||
*/
|
||||
async connectStream(input: AsyncGenerator<Buffer | string, void>): Promise<AsyncGenerator<Buffer, void>> {
|
||||
let cp: InteractiveTerminal | NoninteractiveTerminal = null;
|
||||
const queue = createAsyncQueue<Buffer>();
|
||||
|
||||
function registerChildListeners() {
|
||||
cp.onExit(() => queue.end());
|
||||
|
||||
let bufferedLength = 0;
|
||||
const MAX_BUFFERED_LENGTH = 64000;
|
||||
cp.onData(async data => {
|
||||
const buffer = Buffer.from(data);
|
||||
bufferedLength += buffer.length;
|
||||
const promise = queue.enqueue(buffer).then(() => bufferedLength -= buffer.length);
|
||||
if (bufferedLength >= MAX_BUFFERED_LENGTH) {
|
||||
cp.pause();
|
||||
await promise;
|
||||
if (bufferedLength < MAX_BUFFERED_LENGTH)
|
||||
cp.resume();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function* generator() {
|
||||
try {
|
||||
while (true) {
|
||||
const buffers = queue.clear();
|
||||
if (buffers.length) {
|
||||
yield Buffer.concat(buffers);
|
||||
continue;
|
||||
}
|
||||
|
||||
yield await queue.dequeue();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
cp?.kill();
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
for await (const message of input) {
|
||||
if (!message)
|
||||
continue;
|
||||
|
||||
if (Buffer.isBuffer(message)) {
|
||||
cp?.write(message);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(message.toString());
|
||||
if (parsed.dim) {
|
||||
cp?.resize(parsed.dim.cols, parsed.dim.rows);
|
||||
} else if (parsed.eof) {
|
||||
cp?.sendEOF();
|
||||
} else if ("interactive" in parsed && !cp) {
|
||||
if (parsed.interactive) {
|
||||
try {
|
||||
const spawn = require('node-pty-prebuilt-multiarch').spawn as typeof ptySpawn;
|
||||
cp = new InteractiveTerminal(parsed.cmd, spawn);
|
||||
}
|
||||
catch (e) {
|
||||
this.console.error('Error starting pty', e);
|
||||
queue.end(e);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
cp = new NoninteractiveTerminal(parsed.cmd);
|
||||
}
|
||||
registerChildListeners();
|
||||
}
|
||||
} catch {
|
||||
cp?.write(Buffer.from(message));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
this.console.log(e);
|
||||
cp?.kill();
|
||||
}
|
||||
})();
|
||||
|
||||
return generator();
|
||||
}
|
||||
}
|
||||
88
plugins/core/ui/package-lock.json
generated
88
plugins/core/ui/package-lock.json
generated
@@ -19,7 +19,7 @@
|
||||
"apexcharts": "^3.28.3",
|
||||
"axios": "^0.19.2",
|
||||
"bn.js": "^5.2.1",
|
||||
"core-js": "^2.6.12",
|
||||
"core-js": "^3.33.3",
|
||||
"draggabilly": "^2.3.0",
|
||||
"engine.io-client": "^5.2.0",
|
||||
"feather-icons": "^4.28.0",
|
||||
@@ -96,7 +96,7 @@
|
||||
"sass-loader": "^10.2.0",
|
||||
"stylus": "^0.54.8",
|
||||
"stylus-loader": "^3.0.1",
|
||||
"typescript": "^4.8.2",
|
||||
"typescript": "^5.2.2",
|
||||
"vue-cli-plugin-vuetify": "^2.4.2",
|
||||
"vue-cli-plugin-webpack-bundle-analyzer": "~4.0.0",
|
||||
"vue-template-compiler": "^2.7.14",
|
||||
@@ -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.103",
|
||||
"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",
|
||||
@@ -158,7 +158,7 @@
|
||||
},
|
||||
"../../../sdk/types": {
|
||||
"name": "@scrypted/types",
|
||||
"version": "0.2.94",
|
||||
"version": "0.3.4",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/rimraf": "^3.0.2",
|
||||
@@ -3230,17 +3230,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/babel-preset-app/node_modules/core-js": {
|
||||
"version": "3.32.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.1.tgz",
|
||||
"integrity": "sha512-lqufgNn9NLnESg5mQeYsxQP5w7wrViSj0jr/kv6ECQiByzQkrn1MKvV0L3acttpDqfQrHLwr2KCMgX5b8X+lyQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/babel-preset-jsx": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@vue/babel-preset-jsx/-/babel-preset-jsx-1.4.0.tgz",
|
||||
@@ -5498,6 +5487,14 @@
|
||||
"regenerator-runtime": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-runtime/node_modules/core-js": {
|
||||
"version": "2.6.12",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
|
||||
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
|
||||
"deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
|
||||
"dev": true,
|
||||
"hasInstallScript": true
|
||||
},
|
||||
"node_modules/babel-runtime/node_modules/regenerator-runtime": {
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
|
||||
@@ -7181,11 +7178,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "2.6.12",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
|
||||
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
|
||||
"deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
|
||||
"hasInstallScript": true
|
||||
"version": "3.33.3",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.3.tgz",
|
||||
"integrity": "sha512-lo0kOocUlLKmm6kv/FswQL8zbkH7mVsLJ/FULClOhv8WRVmKLVcs6XPNQAzstfeJTCHMyButEwG+z1kHxHoDZw==",
|
||||
"hasInstallScript": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/core-js-compat": {
|
||||
"version": "3.32.1",
|
||||
@@ -9480,16 +9480,6 @@
|
||||
"core-js": "^3.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/feather-icons/node_modules/core-js": {
|
||||
"version": "3.32.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.1.tgz",
|
||||
"integrity": "sha512-lqufgNn9NLnESg5mQeYsxQP5w7wrViSj0jr/kv6ECQiByzQkrn1MKvV0L3acttpDqfQrHLwr2KCMgX5b8X+lyQ==",
|
||||
"hasInstallScript": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/figgy-pudding": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
|
||||
@@ -17704,16 +17694,16 @@
|
||||
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
||||
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/uc.micro": {
|
||||
@@ -18178,16 +18168,6 @@
|
||||
"vue": "^2.5.18"
|
||||
}
|
||||
},
|
||||
"node_modules/v-calendar/node_modules/core-js": {
|
||||
"version": "3.32.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.1.tgz",
|
||||
"integrity": "sha512-lqufgNn9NLnESg5mQeYsxQP5w7wrViSj0jr/kv6ECQiByzQkrn1MKvV0L3acttpDqfQrHLwr2KCMgX5b8X+lyQ==",
|
||||
"hasInstallScript": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/v8-compile-cache": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz",
|
||||
@@ -18801,16 +18781,6 @@
|
||||
"vue-property-decorator": "^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-slider-component/node_modules/core-js": {
|
||||
"version": "3.32.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.1.tgz",
|
||||
"integrity": "sha512-lqufgNn9NLnESg5mQeYsxQP5w7wrViSj0jr/kv6ECQiByzQkrn1MKvV0L3acttpDqfQrHLwr2KCMgX5b8X+lyQ==",
|
||||
"hasInstallScript": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-style-loader": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"apexcharts": "^3.28.3",
|
||||
"axios": "^0.19.2",
|
||||
"bn.js": "^5.2.1",
|
||||
"core-js": "^2.6.12",
|
||||
"core-js": "^3.33.3",
|
||||
"draggabilly": "^2.3.0",
|
||||
"engine.io-client": "^5.2.0",
|
||||
"feather-icons": "^4.28.0",
|
||||
@@ -99,7 +99,7 @@
|
||||
"sass-loader": "^10.2.0",
|
||||
"stylus": "^0.54.8",
|
||||
"stylus-loader": "^3.0.1",
|
||||
"typescript": "^4.8.2",
|
||||
"typescript": "^5.2.2",
|
||||
"vue-cli-plugin-vuetify": "^2.4.2",
|
||||
"vue-cli-plugin-webpack-bundle-analyzer": "~4.0.0",
|
||||
"vue-template-compiler": "^2.7.14",
|
||||
|
||||
@@ -173,7 +173,7 @@ export default {
|
||||
const version = await info.getVersion();
|
||||
const scryptedEnv = await info.getScryptedEnv();
|
||||
this.currentVersion = version;
|
||||
const { updateAvailable } = await checkServerUpdate(version, scryptedEnv.SCRYPTED_INSTALL_ENVIRONMENT);
|
||||
const { updateAvailable } = await checkServerUpdate(this.$scrypted.mediaManager, version, scryptedEnv.SCRYPTED_INSTALL_ENVIRONMENT);
|
||||
this.updateAvailable = updateAvailable;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,9 @@
|
||||
><v-toolbar-title>Server Management</v-toolbar-title></v-toolbar
|
||||
>
|
||||
<v-card-actions>
|
||||
<v-btn text href="/web/component/backup" 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 }">
|
||||
@@ -126,6 +129,22 @@ export default {
|
||||
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]);
|
||||
await fetch('/web/component/restore', {
|
||||
method: 'POST',
|
||||
body: fileBlob,
|
||||
});
|
||||
},
|
||||
async checkUpdateAvailable() {
|
||||
const info = await this.$scrypted.systemManager.getComponent("info");
|
||||
const version = await info.getVersion();
|
||||
@@ -141,7 +160,7 @@ export default {
|
||||
// old scrypted servers dont support this call, or it may be unimplemented
|
||||
// in which case fall back and determine what the install type is.
|
||||
const scryptedEnv = await info.getScryptedEnv();
|
||||
const { updateAvailable } = await checkServerUpdate(version, scryptedEnv.SCRYPTED_INSTALL_ENVIRONMENT);
|
||||
const { updateAvailable } = await checkServerUpdate(this.$scrypted.mediaManager, version, scryptedEnv.SCRYPTED_INSTALL_ENVIRONMENT);
|
||||
this.updateAvailable = updateAvailable;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -7,11 +7,9 @@
|
||||
<script>
|
||||
import { Terminal } from "xterm";
|
||||
import { FitAddon } from "xterm-addon-fit";
|
||||
import eio from "engine.io-client";
|
||||
import { getCurrentBaseUrl } from "../../../../../../packages/client/src";
|
||||
import { createAsyncQueue } from "@scrypted/common/src/async-queue";
|
||||
|
||||
export default {
|
||||
socket: null,
|
||||
mounted() {
|
||||
const term = new Terminal({
|
||||
theme: this.$vuetify.theme.dark
|
||||
@@ -28,29 +26,32 @@ export default {
|
||||
term.open(this.$refs.terminal);
|
||||
fitAddon.fit();
|
||||
|
||||
const baseUrl = getCurrentBaseUrl();
|
||||
const eioPath = `engine.io/shell`;
|
||||
const eioEndpoint = baseUrl ? new URL(eioPath, baseUrl).pathname : '/' + eioPath;
|
||||
const options = {
|
||||
path: eioEndpoint,
|
||||
};
|
||||
const rootLocation = `${window.location.protocol}//${window.location.host}`;
|
||||
this.socket = eio(rootLocation, options);
|
||||
|
||||
this.socket.on("message", (data) => {
|
||||
term.write(new Uint8Array(Buffer.from(data)));
|
||||
});
|
||||
|
||||
term.onData((data) => {
|
||||
this.socket.send(data);
|
||||
});
|
||||
|
||||
term.onBinary((data) => {
|
||||
this.socket.send(data);
|
||||
});
|
||||
this.setupShell(term);
|
||||
},
|
||||
destroyed() {
|
||||
this.socket?.close();
|
||||
methods: {
|
||||
async setupShell(term) {
|
||||
const termSvcRaw = this.$scrypted.systemManager.getDeviceByName("@scrypted/core");
|
||||
const termSvc = await termSvcRaw.getDevice("terminalservice");
|
||||
const termSvcDirect = await this.$scrypted.connectRPCObject(termSvc);
|
||||
const queue = createAsyncQueue();
|
||||
|
||||
queue.enqueue(JSON.stringify({ interactive: true }));
|
||||
queue.enqueue(JSON.stringify({ 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 })));
|
||||
|
||||
const localGenerator = queue.queue;
|
||||
const remoteGenerator = await termSvcDirect.connectStream(localGenerator);
|
||||
|
||||
for await (const message of remoteGenerator) {
|
||||
if (!message) {
|
||||
break;
|
||||
}
|
||||
term.write(new Uint8Array(Buffer.from(message)));
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -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(':');
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user