mirror of
https://github.com/koush/scrypted.git
synced 2026-02-03 22:23:27 +00:00
Compare commits
741 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1fb0c01e7e | ||
|
|
014d7b35ac | ||
|
|
b08267dab0 | ||
|
|
97d78516f2 | ||
|
|
360c2437c1 | ||
|
|
0b230bfc74 | ||
|
|
d25dc8d266 | ||
|
|
5f4d1e99cd | ||
|
|
ee38ef7817 | ||
|
|
80af38d3e1 | ||
|
|
2f19866f05 | ||
|
|
cf1c500e9d | ||
|
|
9a770e9dc9 | ||
|
|
6dbb8863a0 | ||
|
|
5eac8d0ab9 | ||
|
|
272bad8f29 | ||
|
|
83a3352862 | ||
|
|
4d5a693208 | ||
|
|
70e7f944c0 | ||
|
|
5a52c03a3d | ||
|
|
f9f597ef01 | ||
|
|
2e07788c0c | ||
|
|
9c0fbc1cb6 | ||
|
|
239d49899d | ||
|
|
2d3589b5a3 | ||
|
|
96ec465a38 | ||
|
|
5bb6b87c7d | ||
|
|
fcfedccaf8 | ||
|
|
98373833fd | ||
|
|
03588be125 | ||
|
|
cdd81daec5 | ||
|
|
d64f90c0c8 | ||
|
|
ec31dee36e | ||
|
|
11f2e88590 | ||
|
|
bf51ddb2d5 | ||
|
|
26000f1828 | ||
|
|
f65485af97 | ||
|
|
72c5690d05 | ||
|
|
e076d61122 | ||
|
|
7071808514 | ||
|
|
1e2fd46cd3 | ||
|
|
e3cdd4326f | ||
|
|
227f932ad8 | ||
|
|
67cec188ce | ||
|
|
1ee276185e | ||
|
|
42ed855b05 | ||
|
|
93da4eed30 | ||
|
|
a72a596578 | ||
|
|
72663dd68c | ||
|
|
108d57dbdd | ||
|
|
bc71fd8515 | ||
|
|
a51070767b | ||
|
|
269cc4dbc9 | ||
|
|
684961fa4b | ||
|
|
4f60b7e379 | ||
|
|
5d72061151 | ||
|
|
f2c940c1d3 | ||
|
|
7e817b0b30 | ||
|
|
75bb15d3b7 | ||
|
|
ba1a1eff67 | ||
|
|
5432b5b917 | ||
|
|
f677cf7393 | ||
|
|
bdf9278131 | ||
|
|
0ae93a9c3f | ||
|
|
72422cdd8b | ||
|
|
390d1b3329 | ||
|
|
024e99766a | ||
|
|
0160502da8 | ||
|
|
f0d65982de | ||
|
|
1445933bd4 | ||
|
|
508f31c254 | ||
|
|
fd1aa10a2a | ||
|
|
fceed68d75 | ||
|
|
955e780c64 | ||
|
|
452fe20e8f | ||
|
|
9083e16cdb | ||
|
|
840a278e5d | ||
|
|
6d036dbd60 | ||
|
|
d5ba6f34d6 | ||
|
|
0321846c22 | ||
|
|
714747fcee | ||
|
|
e3906da3c4 | ||
|
|
820ef70033 | ||
|
|
0c95f5c052 | ||
|
|
4cfd7c4362 | ||
|
|
1e8126dec8 | ||
|
|
d3fbc58736 | ||
|
|
46113744b3 | ||
|
|
3947624ae0 | ||
|
|
4ac5ded012 | ||
|
|
aadfacf50a | ||
|
|
bb1e0ac82b | ||
|
|
23a15a1533 | ||
|
|
01dd480c01 | ||
|
|
364cae3273 | ||
|
|
8a986ab707 | ||
|
|
ca96959de8 | ||
|
|
2f0ae9ef50 | ||
|
|
8b84bac2c2 | ||
|
|
976ed7f1a5 | ||
|
|
b4e6821da8 | ||
|
|
540b990a08 | ||
|
|
ce75b072da | ||
|
|
5bca9b7156 | ||
|
|
ae4914346b | ||
|
|
b593209558 | ||
|
|
9df399708f | ||
|
|
ea25682488 | ||
|
|
06e25e6a16 | ||
|
|
10847ef3f2 | ||
|
|
78184390ac | ||
|
|
9a0c88ac61 | ||
|
|
646dd3613c | ||
|
|
ab87abb859 | ||
|
|
5ce1a2b406 | ||
|
|
1abda3b425 | ||
|
|
c759becac6 | ||
|
|
b3a16c0000 | ||
|
|
0163a804cd | ||
|
|
ab157b16f1 | ||
|
|
905a9aec21 | ||
|
|
8e63dcdb15 | ||
|
|
05cad811e8 | ||
|
|
69a3e1138b | ||
|
|
9c9e29068b | ||
|
|
b8bb6dfa61 | ||
|
|
809956a2a4 | ||
|
|
0be72a70a5 | ||
|
|
9d03566246 | ||
|
|
7c023dbdf6 | ||
|
|
1f2187fd6a | ||
|
|
83b60b7b2b | ||
|
|
edfdd5c1a8 | ||
|
|
cdd350f52b | ||
|
|
1594364194 | ||
|
|
8dac20ed1c | ||
|
|
20beacb746 | ||
|
|
ac51fa6355 | ||
|
|
05a60831e6 | ||
|
|
dd13fee049 | ||
|
|
31fd833873 | ||
|
|
a0e5dd4c89 | ||
|
|
215daf5af7 | ||
|
|
a82972d967 | ||
|
|
6fd6c7af14 | ||
|
|
6d1cf5d3c1 | ||
|
|
0cfef48954 | ||
|
|
e9722d3875 | ||
|
|
fa8d17bec9 | ||
|
|
d69ec69038 | ||
|
|
106fc1bf58 | ||
|
|
4b055f55e1 | ||
|
|
3a70625308 | ||
|
|
7a382a8eba | ||
|
|
6d520dc4b2 | ||
|
|
40c7132ec0 | ||
|
|
4d2a038f19 | ||
|
|
a8bfdb6610 | ||
|
|
9817b0144e | ||
|
|
f662bd7de4 | ||
|
|
de52cec190 | ||
|
|
9a8e48e3c4 | ||
|
|
0560d857c1 | ||
|
|
4ee72cd074 | ||
|
|
7120ff430f | ||
|
|
167c66f8d6 | ||
|
|
4d98ccf86b | ||
|
|
ff2d1d5f97 | ||
|
|
ebe19532fc | ||
|
|
1294fc291a | ||
|
|
39c637a95f | ||
|
|
2fb6331e7b | ||
|
|
e7fd88bf2a | ||
|
|
96455dc38e | ||
|
|
4301911e86 | ||
|
|
1ddbe2fac8 | ||
|
|
b3276304d2 | ||
|
|
fcf87cc559 | ||
|
|
12c1d02a5b | ||
|
|
216504639b | ||
|
|
6eae1c7de3 | ||
|
|
a5a1959bd0 | ||
|
|
62e23880fd | ||
|
|
9e652c3521 | ||
|
|
97004577f3 | ||
|
|
6f3eac4e43 | ||
|
|
c435e351c7 | ||
|
|
ffc9ca14b5 | ||
|
|
9b349fdadc | ||
|
|
7cf0c427f9 | ||
|
|
2fb4fbab15 | ||
|
|
4a50095049 | ||
|
|
2510fafcf7 | ||
|
|
47897da6fb | ||
|
|
94055d032b | ||
|
|
3e7535cc42 | ||
|
|
8d47e9c473 | ||
|
|
3897e78bdc | ||
|
|
2fbc0c2573 | ||
|
|
1c8fd2399d | ||
|
|
3abb6472a7 | ||
|
|
6a221eee98 | ||
|
|
ad9e9f2d1d | ||
|
|
8c6afde1fc | ||
|
|
b7a8f97198 | ||
|
|
f5fabfeedf | ||
|
|
494f881d05 | ||
|
|
7192c5ddc2 | ||
|
|
b4da52eaa2 | ||
|
|
584ea97b08 | ||
|
|
807ba81d92 | ||
|
|
0e35bac42a | ||
|
|
a5a464e000 | ||
|
|
a3a878cbd5 | ||
|
|
8abdab70e9 | ||
|
|
69fd86a684 | ||
|
|
f0e85f14a9 | ||
|
|
6130b7fa0c | ||
|
|
6dba80c277 | ||
|
|
0f4ff0d4fc | ||
|
|
3d58600c5f | ||
|
|
9c9909e05b | ||
|
|
9c0d253cae | ||
|
|
c1c9fec62f | ||
|
|
27a1c5269a | ||
|
|
c0c938d9c4 | ||
|
|
1dae1834ad | ||
|
|
250b2554d7 | ||
|
|
35de80e94a | ||
|
|
ba2bf5692f | ||
|
|
4684ea6592 | ||
|
|
2ab74bc0f8 | ||
|
|
0a888364b2 | ||
|
|
c6ea727a0c | ||
|
|
96a0a6bd90 | ||
|
|
bf783c7c3c | ||
|
|
cbd11908af | ||
|
|
3367856715 | ||
|
|
16d38906fe | ||
|
|
fb37f9f58d | ||
|
|
7514ccf804 | ||
|
|
267a53e84b | ||
|
|
10a7877522 | ||
|
|
f15526f78d | ||
|
|
524f9122b7 | ||
|
|
c35142a112 | ||
|
|
ae63e6004e | ||
|
|
ab90e2ec02 | ||
|
|
96d536f4b2 | ||
|
|
c678b31f6f | ||
|
|
0315466b0a | ||
|
|
0db3b7df5a | ||
|
|
00d8054de8 | ||
|
|
3907547c6f | ||
|
|
bd3bc0dcb3 | ||
|
|
b36783df0a | ||
|
|
b676c27316 | ||
|
|
bcea7b869b | ||
|
|
2dd549c042 | ||
|
|
c06e3623b6 | ||
|
|
008e0ecbf7 | ||
|
|
e6cb41168f | ||
|
|
95ac72c5c8 | ||
|
|
faa667f622 | ||
|
|
32868c69fe | ||
|
|
207cb9d833 | ||
|
|
f2de58f59a | ||
|
|
484682257b | ||
|
|
b0b922d209 | ||
|
|
e37295fb20 | ||
|
|
2e72366d41 | ||
|
|
97b09442e8 | ||
|
|
c2defb8c08 | ||
|
|
aa255530aa | ||
|
|
0b26f4df39 | ||
|
|
be98083557 | ||
|
|
f4dcb8e662 | ||
|
|
45186316a6 | ||
|
|
c6e6c881fe | ||
|
|
62b07ea609 | ||
|
|
a00ae60ab0 | ||
|
|
878753a526 | ||
|
|
3c1801ad01 | ||
|
|
30f9e358b7 | ||
|
|
456faea1fd | ||
|
|
5e58b1426e | ||
|
|
ec6d617c09 | ||
|
|
1238abedb1 | ||
|
|
3e18b9e6aa | ||
|
|
dce76b5d87 | ||
|
|
de645dfacb | ||
|
|
6fd66db896 | ||
|
|
62850163d7 | ||
|
|
b46a385a81 | ||
|
|
c94fb231c6 | ||
|
|
a3df934a88 | ||
|
|
a6143e103e | ||
|
|
df705cb0e7 | ||
|
|
6e7f291f81 | ||
|
|
fa5b9f66db | ||
|
|
f760840a6d | ||
|
|
f36ee6ccb5 | ||
|
|
bb610f2bb1 | ||
|
|
6182369804 | ||
|
|
70c4d62466 | ||
|
|
c20c960a4c | ||
|
|
da95729299 | ||
|
|
35444f3f1a | ||
|
|
8dbf751cd9 | ||
|
|
e9eecd145e | ||
|
|
94350669b1 | ||
|
|
5876fe9ff5 | ||
|
|
04cd033565 | ||
|
|
1c3bfc5acb | ||
|
|
41a09629bf | ||
|
|
fa4cf60c21 | ||
|
|
b2848c1496 | ||
|
|
514483c69c | ||
|
|
6e73f2d95f | ||
|
|
4535e9f50f | ||
|
|
12fc6b1619 | ||
|
|
f0402564a8 | ||
|
|
86d900a299 | ||
|
|
2cde2b6824 | ||
|
|
ff0350abb9 | ||
|
|
6c6d2ba40e | ||
|
|
857cc656bd | ||
|
|
776356fc02 | ||
|
|
50d9cee8ea | ||
|
|
1cb5e43f90 | ||
|
|
c8df32e6ae | ||
|
|
77c30b4907 | ||
|
|
96ae2fc89e | ||
|
|
a54978e3f0 | ||
|
|
807b9c1950 | ||
|
|
be05127147 | ||
|
|
ac1134aa41 | ||
|
|
0487c95e00 | ||
|
|
8add1419e9 | ||
|
|
50d980cc01 | ||
|
|
3488a3b4ec | ||
|
|
b3abf5af9b | ||
|
|
d494f46739 | ||
|
|
d3729f3ae7 | ||
|
|
a2fb900166 | ||
|
|
706e37ea68 | ||
|
|
b7509fbd12 | ||
|
|
d994f7c900 | ||
|
|
4e21db52e2 | ||
|
|
a35fd3b79b | ||
|
|
eebcf1aac5 | ||
|
|
704145ce5d | ||
|
|
8f2e15f9df | ||
|
|
c5cf8d01ea | ||
|
|
3356777021 | ||
|
|
544570d435 | ||
|
|
e6b9eb6fb5 | ||
|
|
64137c796e | ||
|
|
3d29478f24 | ||
|
|
862db817db | ||
|
|
7fcc61609e | ||
|
|
4448d82b48 | ||
|
|
370b63584a | ||
|
|
fda778cdaa | ||
|
|
58d2e14542 | ||
|
|
577c6a1733 | ||
|
|
03c4dd5ecc | ||
|
|
5b1889e77b | ||
|
|
d9203318e2 | ||
|
|
dbce9dac03 | ||
|
|
6b5755cc4d | ||
|
|
8aee8f39a3 | ||
|
|
7e9c23b490 | ||
|
|
a87a88db2a | ||
|
|
3af233cd4c | ||
|
|
67347817fe | ||
|
|
f1121500e1 | ||
|
|
a1cbfe7d26 | ||
|
|
1fa2cae936 | ||
|
|
7490188986 | ||
|
|
374c5364f4 | ||
|
|
719c8af9c4 | ||
|
|
45c7117cd4 | ||
|
|
ff095a6157 | ||
|
|
a04aa566a2 | ||
|
|
ec8344be7f | ||
|
|
e21ac6283b | ||
|
|
a23a73942d | ||
|
|
e90e9cd2e8 | ||
|
|
8308d5fa46 | ||
|
|
acaebd5c48 | ||
|
|
a79bd66969 | ||
|
|
f37b21c0b2 | ||
|
|
868403ecde | ||
|
|
00aa766a6b | ||
|
|
5bad16859a | ||
|
|
ebf7063422 | ||
|
|
441361e1ec | ||
|
|
3fb519e3b2 | ||
|
|
fd67756ec6 | ||
|
|
1f7625ca60 | ||
|
|
5640a55507 | ||
|
|
432eb8367e | ||
|
|
59d2657002 | ||
|
|
9012eb9192 | ||
|
|
2918c8fd21 | ||
|
|
90c8e90af7 | ||
|
|
b83b5196da | ||
|
|
239124cbdc | ||
|
|
f6d931a1eb | ||
|
|
8e37623695 | ||
|
|
2f2c6545a4 | ||
|
|
f8669ea693 | ||
|
|
1cb9985cf8 | ||
|
|
3e3e6504bf | ||
|
|
4856193e35 | ||
|
|
28166a1abc | ||
|
|
97de3c7bf6 | ||
|
|
8e75979f07 | ||
|
|
4c8eb9639f | ||
|
|
7a0d070c04 | ||
|
|
3052b954bf | ||
|
|
5f715669ee | ||
|
|
0de1c6bdd5 | ||
|
|
02f69c3077 | ||
|
|
af5d83ecc0 | ||
|
|
2143b4e2c2 | ||
|
|
86d38b5081 | ||
|
|
a61be80b24 | ||
|
|
97e31ec51d | ||
|
|
dd1efe0756 | ||
|
|
155cc89239 | ||
|
|
76cdbc6e96 | ||
|
|
c68a0286e8 | ||
|
|
90a7e44704 | ||
|
|
35031427b2 | ||
|
|
954c7789ba | ||
|
|
b9c4e1cd16 | ||
|
|
cd7e60781c | ||
|
|
4ae2de0467 | ||
|
|
2fdf58db31 | ||
|
|
27af54e929 | ||
|
|
b7de4d92cf | ||
|
|
82544d2c1b | ||
|
|
61c32571d8 | ||
|
|
da8032f922 | ||
|
|
e016011f5a | ||
|
|
d8332898f7 | ||
|
|
6903d56570 | ||
|
|
0fa8a728f7 | ||
|
|
7081cd6605 | ||
|
|
83f24ebdaa | ||
|
|
958442b1bd | ||
|
|
b320fd425b | ||
|
|
0e1305ec5e | ||
|
|
1c3c75db33 | ||
|
|
afd4927e5b | ||
|
|
1b647c902f | ||
|
|
45af364215 | ||
|
|
c5f33f8eb5 | ||
|
|
cb7ea1c624 | ||
|
|
e1571e62d3 | ||
|
|
3065ffef94 | ||
|
|
9c0a59a75a | ||
|
|
e75c183511 | ||
|
|
e50c730c9f | ||
|
|
2da94cdc97 | ||
|
|
b4293e3363 | ||
|
|
71ce995276 | ||
|
|
54d73f6692 | ||
|
|
6ebab812b4 | ||
|
|
9d921544ab | ||
|
|
93c371841c | ||
|
|
a73f421cee | ||
|
|
090362b0ce | ||
|
|
73607fd1aa | ||
|
|
09c8c114f7 | ||
|
|
dae40ba862 | ||
|
|
fd48eee7b2 | ||
|
|
ec19410e0c | ||
|
|
4e5f6885a9 | ||
|
|
f41a6383ae | ||
|
|
644f7d3304 | ||
|
|
e2067c156b | ||
|
|
d325df083f | ||
|
|
04de63ae8e | ||
|
|
803a2d7c51 | ||
|
|
a719026e01 | ||
|
|
1ac5b992d6 | ||
|
|
6766b35438 | ||
|
|
548ff489c4 | ||
|
|
cb742ab75e | ||
|
|
625c1d4e57 | ||
|
|
88604bcdcb | ||
|
|
441cd0d169 | ||
|
|
2f1c45f9bd | ||
|
|
c3bb9c96de | ||
|
|
954be25d0c | ||
|
|
0b28454048 | ||
|
|
349c41657a | ||
|
|
acc7f0c4db | ||
|
|
36ee539f0c | ||
|
|
628c084764 | ||
|
|
c541aa8b3b | ||
|
|
0dc719ca0d | ||
|
|
ead2c5e76f | ||
|
|
dbb314b4eb | ||
|
|
a05bcd6ce4 | ||
|
|
3a6b244a4a | ||
|
|
d6b9900db5 | ||
|
|
8fa5e23797 | ||
|
|
41d042b5bd | ||
|
|
81b235c548 | ||
|
|
657921a5b3 | ||
|
|
a47f7e2566 | ||
|
|
eec6291d9e | ||
|
|
064da326c0 | ||
|
|
cbf95e1186 | ||
|
|
70aa5b75bf | ||
|
|
dfe34947cb | ||
|
|
f7c0091b7c | ||
|
|
3dd6f114d4 | ||
|
|
a0a8e25e18 | ||
|
|
32f0d675bc | ||
|
|
1306eda422 | ||
|
|
79f4c27bed | ||
|
|
eb57698c8b | ||
|
|
454d96c5d3 | ||
|
|
85daf72d66 | ||
|
|
9d50ba79f7 | ||
|
|
764fbbb21b | ||
|
|
89e9cf343d | ||
|
|
dd7d920480 | ||
|
|
426454f28f | ||
|
|
66441ee177 | ||
|
|
d3dee3a199 | ||
|
|
b174fbc19b | ||
|
|
88da7fc5b4 | ||
|
|
c8b799f857 | ||
|
|
b28eef9d10 | ||
|
|
f66d39f8d9 | ||
|
|
b6cbc126d6 | ||
|
|
bee77e121e | ||
|
|
a62f402982 | ||
|
|
6c67ac6570 | ||
|
|
abea872714 | ||
|
|
22018ee573 | ||
|
|
640b2d806d | ||
|
|
f317c8d9ee | ||
|
|
7d3d7be1cd | ||
|
|
1ec954ac98 | ||
|
|
7b2ce12f13 | ||
|
|
3da2e48cf3 | ||
|
|
50fcb6aeab | ||
|
|
952b90fc98 | ||
|
|
0ff95581b1 | ||
|
|
2e02e7f4ef | ||
|
|
fcc51418c3 | ||
|
|
5c31e75f3d | ||
|
|
f590675198 | ||
|
|
23ba720d4f | ||
|
|
a64f3e8082 | ||
|
|
a8eb1a21d7 | ||
|
|
aa6cd770f8 | ||
|
|
a06e0d9138 | ||
|
|
807a894eac | ||
|
|
b1f216b671 | ||
|
|
7d25053b5a | ||
|
|
3fe020c443 | ||
|
|
16812680d8 | ||
|
|
36b36081eb | ||
|
|
baf65a0d33 | ||
|
|
21ab560671 | ||
|
|
370401f034 | ||
|
|
911e56f6fc | ||
|
|
2cc229d39c | ||
|
|
efd9afd1ea | ||
|
|
3c49b87b44 | ||
|
|
dc5bbc375b | ||
|
|
962ceb549e | ||
|
|
3e47855bc6 | ||
|
|
6996027626 | ||
|
|
932b84d1e5 | ||
|
|
801bd46730 | ||
|
|
e5a764d82f | ||
|
|
7c9cd9f112 | ||
|
|
59e1391fae | ||
|
|
e0a6e66e8a | ||
|
|
fa7071b335 | ||
|
|
c28e60d875 | ||
|
|
62cbb88207 | ||
|
|
4b6a858f2b | ||
|
|
97a254b5d2 | ||
|
|
0ada6286e7 | ||
|
|
9f12e6dd6e | ||
|
|
604798e845 | ||
|
|
d912266de1 | ||
|
|
f5a32489d7 | ||
|
|
135ad8e3a8 | ||
|
|
3c4021c66b | ||
|
|
669ab17772 | ||
|
|
1860d7d8ea | ||
|
|
fa266e9dd1 | ||
|
|
4e2f3bf2c7 | ||
|
|
146e27fd13 | ||
|
|
e4bb50375f | ||
|
|
9686315c02 | ||
|
|
520895f3aa | ||
|
|
ddffc49bcf | ||
|
|
a07f52445d | ||
|
|
5e7b203f11 | ||
|
|
d752298960 | ||
|
|
5253f29831 | ||
|
|
58d674746d | ||
|
|
8a640758d1 | ||
|
|
9be913af26 | ||
|
|
da17bee516 | ||
|
|
48d9790051 | ||
|
|
c43014348d | ||
|
|
cec3a592ba | ||
|
|
c446ddcdf4 | ||
|
|
72f79ea8ef | ||
|
|
41988699d0 | ||
|
|
5151c520d4 | ||
|
|
e1abe717fa | ||
|
|
c7a9ca06be | ||
|
|
9827f15f5f | ||
|
|
d245a722e2 | ||
|
|
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 |
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -7,12 +7,21 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# Github Issues is not a Forum
|
||||
# Github Issues are not a Support or Discussion 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.
|
||||
Before opening an issue, view the device's Console logs in the Scrypted Management web interface.
|
||||
|
||||
**DO NOT OPEN ISSUES FOR ANY OF THE FOLLOWING:**
|
||||
|
||||
* Server or hardware setup assistance. Use Discord, Reddit, or Github Discussions.
|
||||
* Feature Requests. Use Discord, Reddit, or Github Discussions.
|
||||
* Packet loss in your camera logs. This is wifi/network congestion.
|
||||
* HomeKit weirdness. See HomeKit troubleshooting guide.
|
||||
* Release schedules or timelines. Releases are rolled out unevenly across the different server platforms.
|
||||
|
||||
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.
|
||||
@@ -34,16 +43,15 @@ A clear and concise description of what you expected to happen.
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
**Server (please complete the following information):**
|
||||
- OS: [e.g. Ubuntu]
|
||||
- Installation Method: [e.g. Desktop App, Docker, Local]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
**Hardware Model (please complete the following information):**
|
||||
- Device: [e.g. Amcrest]
|
||||
|
||||
**Client (please complete the following information, if applicable):**
|
||||
- Software: [e.g. Home app, NVR app, Alexa, Browser]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
51
.github/workflows/build-plugins-changed.yml
vendored
Normal file
51
.github/workflows/build-plugins-changed.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Build changed plugins
|
||||
|
||||
on:
|
||||
# push:
|
||||
# branches: ["main"]
|
||||
# paths: ["plugins/**"]
|
||||
# pull_request:
|
||||
# paths: ["plugins/**"]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Set up base packages
|
||||
run: ./npm-install.sh
|
||||
|
||||
- name: Build changed plugins
|
||||
run: |
|
||||
# Get the list of changed directories in /plugins
|
||||
changed_dirs=$(git diff --name-only HEAD^ HEAD ./plugins | awk -F/ '{print $2}' | uniq)
|
||||
|
||||
# Loop through each changed directory
|
||||
for dir in $changed_dirs; do
|
||||
pushd "./plugins/$dir"
|
||||
|
||||
if [[ "$dir" == "core" ]]; then
|
||||
# core plugin requires ui to be built
|
||||
pushd "./ui"
|
||||
echo "plugins/$dir/ui > npm install"
|
||||
npm install
|
||||
echo "plugins/$dir/ui > npm run build"
|
||||
npm run build
|
||||
popd
|
||||
fi
|
||||
|
||||
echo "plugins/$dir > npm install"
|
||||
npm install
|
||||
echo "plugins/$dir > npm run build"
|
||||
npm run build
|
||||
popd
|
||||
done
|
||||
25
.github/workflows/build-sdk.yml
vendored
Normal file
25
.github/workflows/build-sdk.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: Build SDK
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths: ["sdk/**"]
|
||||
pull_request:
|
||||
paths: ["sdk/**"]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./sdk
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
- run: npm run build
|
||||
93
.github/workflows/docker-common.yml
vendored
93
.github/workflows/docker-common.yml
vendored
@@ -7,12 +7,12 @@ jobs:
|
||||
build:
|
||||
name: Push Docker image to Docker Hub
|
||||
runs-on: self-hosted
|
||||
# runs-on: ubuntu-latest
|
||||
env:
|
||||
NODE_VERSION: '20'
|
||||
strategy:
|
||||
matrix:
|
||||
NODE_VERSION: ["18", "20"]
|
||||
BASE: ["jammy"]
|
||||
FLAVOR: ["full", "lite", "thin"]
|
||||
FLAVOR: ["full", "lite"]
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3
|
||||
@@ -23,15 +23,23 @@ jobs:
|
||||
- name: Set up SSH
|
||||
uses: MrSquaare/ssh-setup-action@v2
|
||||
with:
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM64 }}
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_AMD64 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up SSH
|
||||
uses: MrSquaare/ssh-setup-action@v2
|
||||
with:
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM7 }}
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM64 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
platforms: linux/amd64
|
||||
append: |
|
||||
- endpoint: ssh://${{ secrets.DOCKER_SSH_USER }}@${{ secrets.DOCKER_SSH_HOST_AMD64 }}
|
||||
platforms: linux/amd64
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
@@ -57,13 +65,84 @@ jobs:
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
build-args: |
|
||||
NODE_VERSION=${{ matrix.NODE_VERSION }}
|
||||
NODE_VERSION=${{ env.NODE_VERSION }}
|
||||
BASE=${{ matrix.BASE }}
|
||||
context: install/docker/
|
||||
file: install/docker/Dockerfile.${{ matrix.FLAVOR }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
koush/scrypted-common:${{ matrix.NODE_VERSION }}-${{ matrix.BASE }}-${{ matrix.FLAVOR }}
|
||||
koush/scrypted-common:${{ matrix.BASE }}-${{ matrix.FLAVOR }}
|
||||
ghcr.io/koush/scrypted-common:${{ matrix.BASE }}-${{ matrix.FLAVOR }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
build-nvidia:
|
||||
name: Push NVIDIA Docker image to Docker Hub
|
||||
needs: build
|
||||
runs-on: self-hosted
|
||||
strategy:
|
||||
matrix:
|
||||
BASE: ["jammy"]
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up SSH
|
||||
uses: MrSquaare/ssh-setup-action@v2
|
||||
with:
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_AMD64 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up SSH
|
||||
uses: MrSquaare/ssh-setup-action@v2
|
||||
with:
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM64 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
platforms: linux/amd64
|
||||
append: |
|
||||
- endpoint: ssh://${{ secrets.DOCKER_SSH_USER }}@${{ secrets.DOCKER_SSH_HOST_AMD64 }}
|
||||
platforms: linux/amd64
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
platforms: linux/arm64
|
||||
append: |
|
||||
- endpoint: ssh://${{ secrets.DOCKER_SSH_USER }}@${{ secrets.DOCKER_SSH_HOST_ARM64 }}
|
||||
platforms: linux/arm64
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Login to Github Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image (scrypted-common)
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
build-args: |
|
||||
BASE=ghcr.io/koush/scrypted-common:${{ matrix.BASE }}-full
|
||||
context: install/docker/
|
||||
file: install/docker/Dockerfile.nvidia
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
koush/scrypted-common:${{ matrix.BASE }}-nvidia
|
||||
ghcr.io/koush/scrypted-common:${{ matrix.BASE }}-nvidia
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
50
.github/workflows/docker.yml
vendored
50
.github/workflows/docker.yml
vendored
@@ -20,14 +20,10 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
BASE: [
|
||||
"18-jammy-full",
|
||||
"18-jammy-lite",
|
||||
# "18-jammy-thin",
|
||||
# "20-jammy-full",
|
||||
# "20-jammy-lite",
|
||||
# "20-jammy-thin",
|
||||
["jammy-nvidia", ".s6"],
|
||||
["jammy-full", ".s6"],
|
||||
["jammy-lite", ""],
|
||||
]
|
||||
SUPERVISOR: ["", ".s6"]
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3
|
||||
@@ -49,15 +45,23 @@ jobs:
|
||||
- name: Set up SSH
|
||||
uses: MrSquaare/ssh-setup-action@v2
|
||||
with:
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM64 }}
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_AMD64 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up SSH
|
||||
uses: MrSquaare/ssh-setup-action@v2
|
||||
with:
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM7 }}
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM64 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
platforms: linux/amd64
|
||||
append: |
|
||||
- endpoint: ssh://${{ secrets.DOCKER_SSH_USER }}@${{ secrets.DOCKER_SSH_HOST_AMD64 }}
|
||||
platforms: linux/amd64
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
@@ -83,27 +87,23 @@ jobs:
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
build-args: |
|
||||
BASE=${{ matrix.BASE }}
|
||||
BASE=${{ matrix.BASE[0] }}
|
||||
SCRYPTED_INSTALL_VERSION=${{ steps.package-version.outputs.NPM_VERSION }}
|
||||
context: install/docker/
|
||||
file: install/docker/Dockerfile${{ matrix.SUPERVISOR }}
|
||||
file: install/docker/Dockerfile${{ matrix.BASE[1] }}
|
||||
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) }}
|
||||
${{ matrix.BASE == '18-jammy-full' && matrix.SUPERVISOR == '.s6' && format('koush/scrypted:{0}', github.event.inputs.tag) || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE == '18-jammy-full' && matrix.SUPERVISOR == '' && 'koush/scrypted:full' || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE == '18-jammy-lite' && matrix.SUPERVISOR == '' && 'koush/scrypted:lite' || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE == '18-jammy-thin' && matrix.SUPERVISOR == '' && 'koush/scrypted:thin' || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE == '18-jammy-lite' && matrix.SUPERVISOR == '.s6' && 'koush/scrypted:lite-s6' || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE == '18-jammy-thin' && matrix.SUPERVISOR == '.s6' && 'koush/scrypted:thin-s6' || '' }}
|
||||
${{ format('koush/scrypted:v{1}-{0}', matrix.BASE[0], github.event.inputs.publish_tag || steps.package-version.outputs.NPM_VERSION) }}
|
||||
${{ matrix.BASE[0] == 'jammy-full' && format('koush/scrypted:{0}', github.event.inputs.tag) || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE[0] == 'jammy-nvidia' && 'koush/scrypted:nvidia' || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE[0] == 'jammy-full' && 'koush/scrypted:full' || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE[0] == 'jammy-lite' && 'koush/scrypted:lite' || '' }}
|
||||
|
||||
${{ format('ghcr.io/koush/scrypted:{0}{1}-v{2}', matrix.BASE, matrix.SUPERVISOR, github.event.inputs.publish_tag || steps.package-version.outputs.NPM_VERSION) }}
|
||||
${{ matrix.BASE == '18-jammy-full' && matrix.SUPERVISOR == '.s6' && format('ghcr.io/koush/scrypted:{0}', github.event.inputs.tag) || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE == '18-jammy-full' && matrix.SUPERVISOR == '' && 'ghcr.io/koush/scrypted:full' || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE == '18-jammy-lite' && matrix.SUPERVISOR == '' && 'ghcr.io/koush/scrypted:lite' || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE == '18-jammy-thin' && matrix.SUPERVISOR == '' && 'ghcr.io/koush/scrypted:thin' || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE == '18-jammy-lite' && matrix.SUPERVISOR == '.s6' && 'ghcr.io/koush/scrypted:lite-s6' || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE == '18-jammy-thin' && matrix.SUPERVISOR == '.s6' && 'ghcr.io/koush/scrypted:thin-s6' || '' }}
|
||||
${{ format('ghcr.io/koush/scrypted:v{1}-{0}', matrix.BASE[0], github.event.inputs.publish_tag || steps.package-version.outputs.NPM_VERSION) }}
|
||||
${{ matrix.BASE[0] == 'jammy-full' && format('ghcr.io/koush/scrypted:{0}', github.event.inputs.tag) || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE[0] == 'jammy-nvidia' && 'ghcr.io/koush/scrypted:nvidia' || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE[0] == 'jammy-full' && 'ghcr.io/koush/scrypted:full' || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE[0] == 'jammy-lite' && 'ghcr.io/koush/scrypted:lite' || '' }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
64
.github/workflows/test.yml
vendored
64
.github/workflows/test.yml
vendored
@@ -9,52 +9,28 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test_linux_local:
|
||||
name: Test Linux local installation
|
||||
runs-on: ubuntu-latest
|
||||
test_local:
|
||||
name: Test local installation on ${{ matrix.runner }}
|
||||
runs-on: ${{ matrix.runner }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runner: [ubuntu-latest, macos-14, macos-13, windows-latest]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Run install script
|
||||
|
||||
- name: Parse latest server release
|
||||
id: parse_server
|
||||
shell: bash
|
||||
run: |
|
||||
cat ./install/local/install-scrypted-dependencies-linux.sh | sudo SERVICE_USER=$USER bash
|
||||
|
||||
- name: Test server is running
|
||||
run: |
|
||||
systemctl status scrypted.service
|
||||
curl -k --retry 20 --retry-all-errors --retry-max-time 600 https://localhost:10443/
|
||||
|
||||
test_mac_local:
|
||||
name: Test Mac local installation
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Run install script
|
||||
run: |
|
||||
mkdir -p ~/.scrypted
|
||||
bash ./install/local/install-scrypted-dependencies-mac.sh
|
||||
|
||||
- name: Test server is running
|
||||
run: |
|
||||
curl -k --retry 20 --retry-all-errors --retry-max-time 600 https://localhost:10443/
|
||||
|
||||
test_windows_local:
|
||||
name: Test Windows local installation
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Run install script
|
||||
run: |
|
||||
.\install\local\install-scrypted-dependencies-win.ps1
|
||||
|
||||
- name: Test server is running
|
||||
run: |
|
||||
curl -k --retry 20 --retry-all-errors --retry-max-time 600 https://localhost:10443/
|
||||
VERSION=$(cat ./server/package-lock.json | jq -r '.version')
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "Will test @scrypted/server@$VERSION"
|
||||
|
||||
- name: Install scrypted server
|
||||
uses: scryptedapp/setup-scrypted@v0.0.2
|
||||
with:
|
||||
branch: ${{ github.sha }}
|
||||
version: ${{ steps.parse_server.outputs.version }}
|
||||
12
.gitmodules
vendored
12
.gitmodules
vendored
@@ -4,15 +4,6 @@
|
||||
[submodule "plugins/myq/src/myq"]
|
||||
path = plugins/myq/src/myq
|
||||
url = ../../koush/myq.git
|
||||
[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
|
||||
[submodule "external/ring-client-api"]
|
||||
path = external/ring-client-api
|
||||
url = ../../koush/ring
|
||||
@@ -38,3 +29,6 @@
|
||||
[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"
|
||||
},
|
||||
]
|
||||
}
|
||||
1
common/fs/@types/sdk/settings-mixin.d.ts
vendored
Symbolic link
1
common/fs/@types/sdk/settings-mixin.d.ts
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../sdk/dist/src/settings-mixin.d.ts
|
||||
1
common/fs/@types/sdk/storage-settings.d.ts
vendored
Symbolic link
1
common/fs/@types/sdk/storage-settings.d.ts
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../sdk/dist/src/storage-settings.d.ts
|
||||
621
common/package-lock.json
generated
621
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.29",
|
||||
"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,66 +111,94 @@
|
||||
},
|
||||
"../server": {
|
||||
"name": "@scrypted/server",
|
||||
"version": "0.6.19",
|
||||
"version": "0.106.0",
|
||||
"hasInstallScript": true,
|
||||
"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/ffmpeg-static": "^6.1.0-build1",
|
||||
"@scrypted/node-pty": "^1.0.10",
|
||||
"@scrypted/types": "^0.3.28",
|
||||
"adm-zip": "^0.5.12",
|
||||
"body-parser": "^1.20.2",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"debug": "^4.3.4",
|
||||
"engine.io": "^6.2.0",
|
||||
"express": "^4.18.2",
|
||||
"ffmpeg-static": "^5.1.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"engine.io": "^6.5.4",
|
||||
"express": "^4.19.2",
|
||||
"follow-redirects": "^1.15.6",
|
||||
"http-auth": "^4.2.0",
|
||||
"ip": "^1.1.8",
|
||||
"level": "^6.0.1",
|
||||
"linkfs": "^2.1.0",
|
||||
"ip": "^2.0.1",
|
||||
"level": "^8.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"memfs": "^3.4.7",
|
||||
"mime": "^3.0.0",
|
||||
"mkdirp": "^1.0.4",
|
||||
"nan": "^2.17.0",
|
||||
"nan": "^2.19.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.1.0",
|
||||
"py": "npm:@bjia56/portable-python@^0.1.31",
|
||||
"router": "^1.3.8",
|
||||
"semver": "^7.6.2",
|
||||
"sharp": "^0.33.3",
|
||||
"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": "^7.1.0",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.4.5",
|
||||
"whatwg-mimetype": "^4.0.0",
|
||||
"ws": "^8.17.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.7",
|
||||
"@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.17.1",
|
||||
"@types/node-dijkstra": "^2.5.6",
|
||||
"@types/node-forge": "^1.3.11",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/source-map-support": "^0.5.10",
|
||||
"@types/whatwg-mimetype": "^3.0.2",
|
||||
"@types/ws": "^8.5.10"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"node-pty-prebuilt-multiarch": "^0.10.1-pre.5"
|
||||
"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": {
|
||||
@@ -181,120 +209,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 +425,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 +445,175 @@
|
||||
"@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/ffmpeg-static": "^6.1.0-build1",
|
||||
"@scrypted/node-pty": "^1.0.10",
|
||||
"@scrypted/types": "^0.3.28",
|
||||
"@types/adm-zip": "^0.5.5",
|
||||
"@types/cookie-parser": "^1.4.7",
|
||||
"@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.17.1",
|
||||
"@types/node-dijkstra": "^2.5.6",
|
||||
"@types/node-forge": "^1.3.11",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/source-map-support": "^0.5.10",
|
||||
"@types/whatwg-mimetype": "^3.0.2",
|
||||
"@types/ws": "^8.5.10",
|
||||
"adm-zip": "^0.5.12",
|
||||
"body-parser": "^1.20.2",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"debug": "^4.3.4",
|
||||
"engine.io": "^6.2.0",
|
||||
"express": "^4.18.2",
|
||||
"ffmpeg-static": "^5.1.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"engine.io": "^6.5.4",
|
||||
"express": "^4.19.2",
|
||||
"follow-redirects": "^1.15.6",
|
||||
"http-auth": "^4.2.0",
|
||||
"ip": "^1.1.8",
|
||||
"level": "^6.0.1",
|
||||
"linkfs": "^2.1.0",
|
||||
"ip": "^2.0.1",
|
||||
"level": "^8.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"memfs": "^3.4.7",
|
||||
"mime": "^3.0.0",
|
||||
"mkdirp": "^1.0.4",
|
||||
"nan": "^2.17.0",
|
||||
"nan": "^2.19.0",
|
||||
"node-dijkstra": "^2.5.0",
|
||||
"node-forge": "^1.3.1",
|
||||
"node-gyp": "^8.4.1",
|
||||
"node-pty-prebuilt-multiarch": "^0.10.1-pre.5",
|
||||
"router": "^1.3.7",
|
||||
"semver": "^7.3.8",
|
||||
"node-gyp": "^10.1.0",
|
||||
"py": "npm:@bjia56/portable-python@^0.1.31",
|
||||
"router": "^1.3.8",
|
||||
"semver": "^7.6.2",
|
||||
"sharp": "^0.33.3",
|
||||
"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": "^7.1.0",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.4.5",
|
||||
"whatwg-mimetype": "^4.0.0",
|
||||
"ws": "^8.17.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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,5 +73,5 @@ export abstract class AutoenableMixinProvider extends ScryptedDeviceBase {
|
||||
this.storage.setItem('hasEnabledMixin', JSON.stringify(this.hasEnabledMixin));
|
||||
}
|
||||
|
||||
abstract canMixin(type: ScryptedDeviceType, interfaces: string[]): Promise<string[]>;
|
||||
abstract canMixin(type: ScryptedDeviceType, interfaces: string[]): Promise<string[] | null | undefined | void>;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
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, ScryptedMimeTypes } from "@scrypted/sdk";
|
||||
import { StorageSettings } from "@scrypted/sdk/storage-settings";
|
||||
import { SettingsMixinDeviceBase } from "@scrypted/sdk/settings-mixin";
|
||||
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';
|
||||
|
||||
const { systemManager, deviceManager, mediaManager, endpointManager } = sdk;
|
||||
|
||||
@@ -26,10 +24,18 @@ 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 settingsMixinDefs = readFileAsString('@types/sdk/settings-mixin.d.ts');
|
||||
const storageSettingsDefs = readFileAsString('@types/sdk/storage-settings.d.ts');
|
||||
const scryptedTypesDefs = readFileAsString('@types/sdk/types.d.ts');
|
||||
const scryptedIndexDefs = readFileAsString('@types/sdk/index.d.ts');
|
||||
return {
|
||||
settingsMixinDefs,
|
||||
storageSettingsDefs,
|
||||
scryptedIndexDefs,
|
||||
scryptedTypesDefs,
|
||||
};
|
||||
@@ -58,18 +64,13 @@ 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,
|
||||
StorageSettings,
|
||||
systemManager,
|
||||
deviceManager,
|
||||
endpointManager,
|
||||
mediaManager,
|
||||
@@ -78,6 +79,8 @@ export async function scryptedEval(device: ScryptedDeviceBase, script: string, e
|
||||
localStorage: device.storage,
|
||||
device,
|
||||
exports: {} as any,
|
||||
SettingsMixinDeviceBase,
|
||||
ScryptedMimeTypes,
|
||||
ScryptedInterface,
|
||||
ScryptedDeviceType,
|
||||
// @ts-expect-error
|
||||
@@ -104,7 +107,6 @@ export async function scryptedEval(device: ScryptedDeviceBase, script: string, e
|
||||
return {
|
||||
value,
|
||||
defaultExport,
|
||||
apiProxy,
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
@@ -115,11 +117,20 @@ 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 safeLibs: any = {};
|
||||
|
||||
const safeLibs = {
|
||||
bufferTypeDefs,
|
||||
};
|
||||
for (const safeLib of [
|
||||
'@types/node/globals.d.ts',
|
||||
'@types/node/buffer.d.ts',
|
||||
'@types/node/process.d.ts',
|
||||
'@types/node/events.d.ts',
|
||||
'@types/node/stream.d.ts',
|
||||
'@types/node/fs.d.ts',
|
||||
'@types/node/net.d.ts',
|
||||
'@types/node/child_process.d.ts',
|
||||
]) {
|
||||
safeLibs[`node_modules/${safeLib}`] = readFileAsString(safeLib)
|
||||
}
|
||||
|
||||
const libs = Object.assign(getTypeDefs(), extraLibs);
|
||||
|
||||
@@ -170,15 +181,27 @@ export function createMonacoEvalDefaults(extraLibs: { [lib: string]: string }) {
|
||||
"node_modules/@types/scrypted__sdk/types/index.d.ts"
|
||||
);
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
libs['settingsMixin'],
|
||||
"node_modules/@types/scrypted__sdk/settings-mixin.d.ts"
|
||||
);
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
libs['storageSettings'],
|
||||
"node_modules/@types/scrypted__sdk/storage-settings.d.ts"
|
||||
);
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
libs['sdk'],
|
||||
"node_modules/@types/scrypted__sdk/index.d.ts"
|
||||
);
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
safeLibs.bufferTypeDefs,
|
||||
"node_modules/@types/node/buffer.d.ts"
|
||||
);
|
||||
for (const lib of Object.keys(safeLibs)) {
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
safeLibs[lib],
|
||||
lib,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return `(function() {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -136,12 +136,17 @@ export async function readLine(readable: Readable) {
|
||||
}
|
||||
|
||||
export async function readString(readable: Readable | Promise<Readable>) {
|
||||
let data = '';
|
||||
const buffer = await readBuffer(readable);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
export async function readBuffer(readable: Readable | Promise<Readable>) {
|
||||
const buffers: Buffer[] = [];
|
||||
readable = await readable;
|
||||
readable.on('data', buffer => {
|
||||
data += buffer.toString();
|
||||
buffers.push(buffer);
|
||||
});
|
||||
readable.resume();
|
||||
await once(readable, 'end')
|
||||
return data;
|
||||
return Buffer.concat(buffers);
|
||||
}
|
||||
|
||||
@@ -223,7 +223,12 @@ export class BrowserSignalingSession implements RTCSignalingSession {
|
||||
}
|
||||
|
||||
if (type === 'offer') {
|
||||
let offer = await this.pc.createOffer({
|
||||
let offer: RTCSessionDescriptionInit = this.pc.localDescription;
|
||||
if (offer) {
|
||||
// fast path for duplicate calls to createLocalDescription
|
||||
return toDescription(this.pc.localDescription);
|
||||
}
|
||||
offer = await this.pc.createOffer({
|
||||
offerToReceiveAudio: !!setup.audio,
|
||||
offerToReceiveVideo: !!setup.video,
|
||||
});
|
||||
@@ -232,7 +237,7 @@ export class BrowserSignalingSession implements RTCSignalingSession {
|
||||
return toDescription(offer);
|
||||
await set;
|
||||
await gatheringPromise;
|
||||
offer = await this.pc.createOffer({
|
||||
offer = this.pc.localDescription || await this.pc.createOffer({
|
||||
offerToReceiveAudio: !!setup.audio,
|
||||
offerToReceiveVideo: !!setup.video,
|
||||
});
|
||||
|
||||
@@ -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';
|
||||
@@ -326,6 +324,7 @@ export class RtspClient extends RtspBase {
|
||||
setupOptions = new Map<number, RtspClientTcpSetupOptions>();
|
||||
issuedTeardown = false;
|
||||
hasGetParameter = true;
|
||||
contentBase: string;
|
||||
|
||||
constructor(public url: string) {
|
||||
super();
|
||||
@@ -363,16 +362,21 @@ 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;
|
||||
if (path) {
|
||||
let fullUrl: string;
|
||||
if (!path) {
|
||||
fullUrl = this.url;
|
||||
}
|
||||
else {
|
||||
// a=control may be a full or "relative" url.
|
||||
if (path.includes('rtsp://') || path.includes('rtsps://')) {
|
||||
if (path.includes('rtsp://') || path.includes('rtsps://') || path === '*') {
|
||||
fullUrl = path;
|
||||
}
|
||||
else {
|
||||
fullUrl = this.contentBase || this.url;
|
||||
|
||||
// strangely, relative RTSP urls do not behave like expected from an HTTP-ish server.
|
||||
// ffmpeg will happily suffix path segments after query strings:
|
||||
// SETUP rtsp://localhost:5554/cam/realmonitor?channel=1&subtype=0/trackID=0 RTSP/1.0
|
||||
@@ -390,7 +394,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 +535,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 +593,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];
|
||||
@@ -644,10 +651,13 @@ export class RtspClient extends RtspBase {
|
||||
}
|
||||
|
||||
async describe(headers?: Headers) {
|
||||
return this.request('DESCRIBE', {
|
||||
const response = await this.request('DESCRIBE', {
|
||||
...(headers || {}),
|
||||
Accept: 'application/sdp',
|
||||
});
|
||||
|
||||
this.contentBase = response.headers['content-base'] || response.headers['content-location'];;
|
||||
return response;
|
||||
}
|
||||
|
||||
async setup(options: RtspClientTcpSetupOptions | RtspClientUdpSetupOptions, headers?: Headers) {
|
||||
|
||||
@@ -172,7 +172,8 @@ export function parseFmtp(msection: string[]) {
|
||||
export type MSection = ReturnType<typeof parseMSection>;
|
||||
export type RTPMap = ReturnType<typeof parseRtpMap>;
|
||||
|
||||
export function parseRtpMap(mlineType: string, rtpmap: string) {
|
||||
export function parseRtpMap(mline: ReturnType<typeof parseMLine>, rtpmap: string) {
|
||||
const mlineType = mline.type;
|
||||
const match = rtpmap?.match(/a=rtpmap:([\d]+) (.*?)\/([\d]+)(\/([\d]+))?/);
|
||||
|
||||
rtpmap = rtpmap?.toLowerCase();
|
||||
@@ -218,9 +219,23 @@ export function parseRtpMap(mlineType: string, rtpmap: string) {
|
||||
codec = 'h265';
|
||||
}
|
||||
else if (!rtpmap && mlineType === 'audio') {
|
||||
// ffmpeg seems to omit the rtpmap type for pcm alaw when creating sdp?
|
||||
// is this the default?
|
||||
codec = 'pcm_alaw';
|
||||
if (mline.payloadTypes?.includes(0)) {
|
||||
codec = 'pcm_mulaw';
|
||||
ffmpegEncoder = 'pcm_mulaw';
|
||||
}
|
||||
else if (mline.payloadTypes?.includes(8)) {
|
||||
codec = 'pcm_alaw';
|
||||
ffmpegEncoder = 'pcm_alaw';
|
||||
}
|
||||
else {
|
||||
// ffmpeg seems to omit the rtpmap type for pcm alaw when creating sdp?
|
||||
// is this the default?
|
||||
// 2/21/2024: the paylaod types are included in the mline, and this is legacy code
|
||||
// that maybe should be updated to use the mline payload types when no rtpmap(s) are available.
|
||||
// https://en.wikipedia.org/wiki/RTP_payload_formats
|
||||
codec = 'pcm_alaw';
|
||||
ffmpegEncoder = 'pcm_alaw';
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -240,9 +255,9 @@ export function parseMSection(msection: string[]) {
|
||||
const control = msection.find(line => line.startsWith(acontrol))?.substring(acontrol.length);
|
||||
const mline = parseMLine(msection[0]);
|
||||
const rawRtpmaps = msection.filter(line => line.startsWith(artpmap));
|
||||
const rtpmaps = rawRtpmaps.map(line => parseRtpMap(mline.type, line));
|
||||
const rtpmaps = rawRtpmaps.map(line => parseRtpMap(mline, line));
|
||||
// if no rtp map is specified, pcm_alaw is used. parsing a null rtpmap is valid.
|
||||
const rtpmap = parseRtpMap(mline.type, rawRtpmaps[0]);
|
||||
const rtpmap = parseRtpMap(mline, rawRtpmaps[0]);
|
||||
const { codec } = rtpmap;
|
||||
let direction: string;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"module": "Node16",
|
||||
"moduleResolution": "Node16",
|
||||
"target": "esnext",
|
||||
"noImplicitAny": true,
|
||||
"outDir": "./dist",
|
||||
|
||||
1
external/axios-digest-auth
vendored
1
external/axios-digest-auth
vendored
Submodule external/axios-digest-auth deleted from e1735135be
1
external/face-api.js
vendored
1
external/face-api.js
vendored
Submodule external/face-api.js deleted from a86687eea2
1
external/scrypted-ffmpeg
vendored
1
external/scrypted-ffmpeg
vendored
Submodule external/scrypted-ffmpeg deleted from 2594e5bc64
2
external/werift
vendored
2
external/werift
vendored
Submodule external/werift updated: a0070297a4...3f24822736
@@ -1,13 +1,12 @@
|
||||
# Home Assistant Addon Configuration
|
||||
name: Scrypted
|
||||
version: "18-jammy-full.s6-v0.72.0"
|
||||
version: "v0.105.0-jammy-full"
|
||||
slug: scrypted
|
||||
description: Scrypted is a high performance home video integration and automation platform
|
||||
url: "https://github.com/koush/scrypted"
|
||||
arch:
|
||||
- amd64
|
||||
- aarch64
|
||||
- armv7
|
||||
init: false
|
||||
ingress: true
|
||||
ingress_port: 11080
|
||||
@@ -29,9 +28,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}
|
||||
ARG BASE="20-jammy-full"
|
||||
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="20240321"
|
||||
|
||||
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
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
# install script.
|
||||
################################################################
|
||||
ARG BASE="jammy"
|
||||
FROM ubuntu:${BASE} as header
|
||||
ARG REPO="ubuntu"
|
||||
FROM ${REPO}:${BASE} as header
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@@ -24,7 +25,7 @@ RUN apt-get update && apt-get -y install \
|
||||
apt-get -y update && \
|
||||
apt-get -y upgrade
|
||||
|
||||
ARG NODE_VERSION=18
|
||||
ARG NODE_VERSION=20
|
||||
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
|
||||
@@ -60,11 +61,7 @@ RUN apt-get -y install \
|
||||
# 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
|
||||
|
||||
################################################################
|
||||
@@ -90,7 +87,6 @@ RUN add-apt-repository -y 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
|
||||
@@ -104,12 +100,12 @@ ENV SCRYPTED_CAN_RESTART="true"
|
||||
ENV SCRYPTED_VOLUME="/server/volume"
|
||||
ENV SCRYPTED_INSTALL_PATH="/server"
|
||||
|
||||
RUN test -f "/usr/bin/ffmpeg"
|
||||
RUN test -f "/usr/bin/ffmpeg" && test -f "/usr/bin/python3" && test -f "/usr/bin/python3.9" && test -f "/usr/bin/python3.10"
|
||||
ENV SCRYPTED_FFMPEG_PATH="/usr/bin/ffmpeg"
|
||||
ENV SCRYPTED_PYTHON_PATH="/usr/bin/python3"
|
||||
ENV SCRYPTED_PYTHON39_PATH="/usr/bin/python3.9"
|
||||
ENV SCRYPTED_PYTHON310_PATH="/usr/bin/python3.10"
|
||||
|
||||
# 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,45 +6,27 @@ 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 && \
|
||||
python3 && \
|
||||
apt-get -y update && \
|
||||
apt-get -y upgrade
|
||||
|
||||
ARG NODE_VERSION=18
|
||||
ARG NODE_VERSION=20
|
||||
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 apt-get -y install \
|
||||
python3 \
|
||||
python3-dev \
|
||||
python3-pip \
|
||||
python3-setuptools \
|
||||
python3-wheel
|
||||
|
||||
# python pip
|
||||
RUN rm -f /usr/lib/python**/EXTERNALLY-MANAGED
|
||||
RUN python3 -m pip install --upgrade pip
|
||||
RUN python3 -m pip install debugpy typing_extensions psutil
|
||||
# intel opencl gpu for openvino
|
||||
RUN curl https://raw.githubusercontent.com/koush/scrypted/main/install/docker/install-intel-graphics.sh | bash
|
||||
|
||||
ENV SCRYPTED_INSTALL_ENVIRONMENT="docker"
|
||||
ENV SCRYPTED_CAN_RESTART="true"
|
||||
ENV SCRYPTED_VOLUME="/server/volume"
|
||||
ENV SCRYPTED_INSTALL_PATH="/server"
|
||||
|
||||
RUN test -f "/usr/bin/ffmpeg"
|
||||
ENV SCRYPTED_FFMPEG_PATH="/usr/bin/ffmpeg"
|
||||
RUN test -f "/usr/bin/python3" && test -f "/usr/bin/python3.10"
|
||||
ENV SCRYPTED_PYTHON_PATH="/usr/bin/python3"
|
||||
ENV SCRYPTED_PYTHON310_PATH="/usr/bin/python3.10"
|
||||
|
||||
# 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,22 +1,6 @@
|
||||
FROM koush/18-jammy-full.s6
|
||||
ARG BASE="ghcr.io/koush/scrypted-common:20-jammy-full"
|
||||
FROM $BASE
|
||||
|
||||
WORKDIR /
|
||||
|
||||
# Install miniconda
|
||||
ENV CONDA_DIR /opt/conda
|
||||
RUN wget --quiet https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda.sh && \
|
||||
/bin/bash ~/miniconda.sh -b -p /opt/conda
|
||||
# Put conda in path so we can use conda activate
|
||||
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
|
||||
# nvidia cudnn/libcublas etc.
|
||||
# for some reason this is not provided by the nvidia container toolkit
|
||||
RUN curl https://raw.githubusercontent.com/koush/scrypted/main/install/docker/install-nvidia-graphics.sh | bash
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
ARG BASE="18-jammy-full"
|
||||
FROM koush/scrypted-common:${BASE}
|
||||
ARG BASE="20-jammy-full"
|
||||
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="20240321"
|
||||
|
||||
CMD npm --prefix /server exec scrypted-serve
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
ARG BASE="jammy"
|
||||
FROM ubuntu:${BASE} as header
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get -y update && \
|
||||
apt-get -y upgrade && \
|
||||
apt-get -y install curl software-properties-common apt-utils ffmpeg
|
||||
|
||||
# switch to nvm?
|
||||
ARG NODE_VERSION=18
|
||||
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"
|
||||
ENV SCRYPTED_VOLUME="/server/volume"
|
||||
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.full . && \
|
||||
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:20-jammy-full.nvidia -f Dockerfile.nvidia .
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
set -x
|
||||
|
||||
NODE_VERSION=18
|
||||
NODE_VERSION=20
|
||||
SCRYPTED_INSTALL_VERSION=beta
|
||||
IMAGE_BASE=jammy
|
||||
FLAVOR=full
|
||||
@@ -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 .
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
version: "3.5"
|
||||
|
||||
# The Scrypted docker-compose.yml file typically resides at:
|
||||
# ~/.scrypted/docker-compose.yml
|
||||
|
||||
@@ -34,17 +32,27 @@ services:
|
||||
- SCRYPTED_WEBHOOK_UPDATE_AUTHORIZATION=Bearer SET_THIS_TO_SOME_RANDOM_TEXT
|
||||
- SCRYPTED_WEBHOOK_UPDATE=http://localhost:10444/v1/update
|
||||
|
||||
# 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")
|
||||
# Avahi can be used for network discovery by passing in the host daemon
|
||||
# or running the daemon inside the container. Choose one or the other.
|
||||
# Uncomment next line to run avahi-daemon inside the container.
|
||||
# See volumes and security_opt section below to use the host daemon.
|
||||
# - SCRYPTED_DOCKER_AVAHI=true
|
||||
|
||||
# Uncomment next 3 lines for Nvidia GPU support.
|
||||
# NVIDIA (Part 1 of 4)
|
||||
# - NVIDIA_VISIBLE_DEVICES=all
|
||||
# - NVIDIA_DRIVER_CAPABILITIES=all
|
||||
|
||||
# NVIDIA (Part 2 of 4)
|
||||
# runtime: nvidia
|
||||
|
||||
# NVIDIA (Part 3 of 4) - Use NVIDIA image, and remove subsequent default image.
|
||||
# image: ghcr.io/koush/scrypted:nvidia
|
||||
image: ghcr.io/koush/scrypted
|
||||
|
||||
volumes:
|
||||
# NVIDIA (Part 4 of 4)
|
||||
# - /etc/OpenCL/vendors/nvidia.icd:/etc/OpenCL/vendors/nvidia.icd
|
||||
|
||||
# Scrypted NVR Storage (Part 3 of 3)
|
||||
|
||||
# Modify to add the additional volume for Scrypted NVR.
|
||||
@@ -59,14 +67,20 @@ services:
|
||||
# volume:
|
||||
# nocopy: true
|
||||
|
||||
# uncomment the following lines to expose Avahi, an mDNS advertiser.
|
||||
# make sure Avahi is running on the host machine, otherwise this will not work.
|
||||
# not compatible with Avahi enabled via SCRYPTED_DOCKER_AVAHI=true
|
||||
# Uncomment the following lines to use Avahi daemon from the host.
|
||||
# Ensure Avahi is running on the host machine:
|
||||
# It can be installed with: sudo apt-get install avahi-daemon
|
||||
# This is not compatible with running avahi inside the container (see above).
|
||||
# Also, uncomment the lines under security_opt
|
||||
# - /var/run/dbus:/var/run/dbus
|
||||
# - /var/run/avahi-daemon/socket:/var/run/avahi-daemon/socket
|
||||
|
||||
# Default volume for the Scrypted database. Typically should not be changed.
|
||||
- ~/.scrypted/volume:/server/volume
|
||||
# Uncomment the following lines to use Avahi daemon from the host
|
||||
# Without this, AppArmor will block the container's attempt to talk to Avahi via dbus
|
||||
# security_opt:
|
||||
# - apparmor:unconfined
|
||||
devices: [
|
||||
# uncomment the common systems devices to pass
|
||||
# them through to docker.
|
||||
@@ -90,15 +104,16 @@ services:
|
||||
container_name: scrypted
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
image: koush/scrypted
|
||||
|
||||
# logging is noisy and will unnecessarily wear on flash storage.
|
||||
# scrypted has per device in memory logging that is preferred.
|
||||
# enable the log file if enhanced debugging is necessary.
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "10"
|
||||
driver: "none"
|
||||
# driver: "json-file"
|
||||
# options:
|
||||
# max-size: "10m"
|
||||
# max-file: "10"
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.scope=scrypted"
|
||||
|
||||
|
||||
@@ -1,13 +1,35 @@
|
||||
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 &&
|
||||
# this script previvously apt install intel-media-va-driver-non-free, but that seems to no longer be necessary.
|
||||
|
||||
# the intel provided script is disabled since it does not work with the 6.8 kernel in Ubuntu 24.04 or Proxmox 8.2.
|
||||
# manual installation of the Intel graphics stuff is required.
|
||||
|
||||
# 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 &&
|
||||
# apt-get -y dist-upgrade;
|
||||
|
||||
# manual installation
|
||||
# https://github.com/intel/compute-runtime/releases/tag/24.13.29138.7
|
||||
|
||||
rm -rf /tmp/neo && mkdir -p /tmp/neo && cd /tmp/neo &&
|
||||
apt-get install -y ocl-icd-libopencl1 &&
|
||||
curl -O -L https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.16510.2/intel-igc-core_1.0.16510.2_amd64.deb &&
|
||||
curl -O -L https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.16510.2/intel-igc-opencl_1.0.16510.2_amd64.deb &&
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.13.29138.7/intel-level-zero-gpu-dbgsym_1.3.29138.7_amd64.ddeb &&
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.13.29138.7/intel-level-zero-gpu_1.3.29138.7_amd64.deb &&
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.13.29138.7/intel-opencl-icd-dbgsym_24.13.29138.7_amd64.ddeb &&
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.13.29138.7/intel-opencl-icd_24.13.29138.7_amd64.deb &&
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.13.29138.7/libigdgmm12_22.3.18_amd64.deb &&
|
||||
dpkg -i *.deb &&
|
||||
cd /tmp && rm -rf /tmp/neo &&
|
||||
apt-get -y dist-upgrade;
|
||||
|
||||
exit $?
|
||||
else
|
||||
echo "Intel graphics will not be installed on this architecture."
|
||||
|
||||
16
install/docker/install-nvidia-graphics.sh
Normal file
16
install/docker/install-nvidia-graphics.sh
Normal file
@@ -0,0 +1,16 @@
|
||||
if [ "$(uname -m)" = "x86_64" ]
|
||||
then
|
||||
echo "Installing NVIDIA graphics packages."
|
||||
apt update -q \
|
||||
&& apt install -y wget \
|
||||
&& wget -qO /cuda-keyring.deb https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/$(uname -m)/cuda-keyring_1.1-1_all.deb \
|
||||
&& dpkg -i /cuda-keyring.deb \
|
||||
&& apt update -q \
|
||||
&& apt install -y cuda-nvcc-11-8 libcublas-11-8 libcudnn8 cuda-libraries-11-8 \
|
||||
&& apt install -y cuda-nvcc-12-4 libcublas-12-4 libcudnn8 cuda-libraries-12-4;
|
||||
exit $?
|
||||
else
|
||||
echo "NVIDIA graphics will not be installed on this architecture."
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -26,6 +26,10 @@ then
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Stopping local service if it is running..."
|
||||
systemctl stop scrypted.service 2> /dev/null
|
||||
systemctl disable scrypted.service 2> /dev/null
|
||||
|
||||
USER_HOME=$(eval echo ~$SERVICE_USER)
|
||||
SCRYPTED_HOME=$USER_HOME/.scrypted
|
||||
mkdir -p $SCRYPTED_HOME
|
||||
@@ -48,21 +52,22 @@ echo "Created $DOCKER_COMPOSE_YML"
|
||||
curl -s https://raw.githubusercontent.com/koush/scrypted/main/install/docker/docker-compose.yml | sed s/SET_THIS_TO_SOME_RANDOM_TEXT/"$(echo $RANDOM | md5sum | head -c 32)"/g > $DOCKER_COMPOSE_YML
|
||||
if [ -d /dev/dri ]
|
||||
then
|
||||
sed -i 's/'#' - \/dev\/dri/- \/dev\/dri/g' $DOCKER_COMPOSE_YML
|
||||
sed -i 's/'#' "\/dev\/dri/"\/dev\/dri/g' $DOCKER_COMPOSE_YML
|
||||
fi
|
||||
|
||||
readyn "Install avahi-daemon? This is the recommended for reliable HomeKit discovery and pairing."
|
||||
if [ "$yn" == "y" ]
|
||||
then
|
||||
sudo apt-get -y install avahi-daemon
|
||||
sed -i 's/'#' - \/var\/run\/dbus/- \/var\/run\/dbus/g' $DOCKER_COMPOSE_YML
|
||||
sed -i 's/'#' - \/var\/run\/avahi-daemon/- \/var\/run\/avahi-daemon/g' $DOCKER_COMPOSE_YML
|
||||
sed -i 's/'#' security_opt:/security_opt:/g' $DOCKER_COMPOSE_YML
|
||||
sed -i 's/'#' - apparmor:unconfined/ - apparmor:unconfined/g' $DOCKER_COMPOSE_YML
|
||||
fi
|
||||
|
||||
echo "Setting permissions on $SCRYPTED_HOME"
|
||||
chown -R $SERVICE_USER $SCRYPTED_HOME
|
||||
|
||||
echo "Optional:"
|
||||
readyn "Edit docker-compose.yml to add external storage for Scrypted NVR?"
|
||||
|
||||
if [ "$yn" == "y" ]
|
||||
then
|
||||
apt install nano
|
||||
nano $DOCKER_COMPOSE_YML
|
||||
fi
|
||||
|
||||
set +e
|
||||
|
||||
echo "docker compose down"
|
||||
@@ -85,3 +90,6 @@ echo "Scrypted is now running at: https://localhost:10443/"
|
||||
echo "Note that it is https and that you'll be asked to approve/ignore the website certificate."
|
||||
echo
|
||||
echo
|
||||
echo "Optional:"
|
||||
echo "Scrypted NVR Recording storage directory can be configured with an additional script:"
|
||||
echo "https://docs.scrypted.app/scrypted-nvr/installation.html#docker-volume"
|
||||
|
||||
159
install/docker/setup-scrypted-nvr-volume.sh
Normal file
159
install/docker/setup-scrypted-nvr-volume.sh
Normal file
@@ -0,0 +1,159 @@
|
||||
|
||||
if [ -z "$SERVICE_USER" ]
|
||||
then
|
||||
echo "Scrypted SERVICE_USER environment variable was not specified. NVR Storage can not be configured."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$USER" != "root" ]
|
||||
then
|
||||
echo "$USER"
|
||||
echo "This script must be run as sudo or root."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
USER_HOME=$(eval echo ~$SERVICE_USER)
|
||||
SCRYPTED_HOME=$USER_HOME/.scrypted
|
||||
DOCKER_COMPOSE_YML=$SCRYPTED_HOME/docker-compose.yml
|
||||
|
||||
if [ ! -f "$DOCKER_COMPOSE_YML" ]
|
||||
then
|
||||
echo "$DOCKER_COMPOSE_YML not found. Install Scrypted first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NVR_MOUNT_LINE=$(cat "$DOCKER_COMPOSE_YML" | grep :/nvr)
|
||||
if [ -z "$NVR_MOUNT_LINE" ]
|
||||
then
|
||||
echo "Unexpected contents in $DOCKER_COMPOSE_YML. Rerun the Scrypted docker compose installer."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function backup() {
|
||||
BACKUP_FILE="$1".scrypted-bak
|
||||
if [ ! -f "$BACKUP_FILE" ]
|
||||
then
|
||||
cp "$1" "$BACKUP_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
backup "$DOCKER_COMPOSE_YML"
|
||||
|
||||
function readyn() {
|
||||
while true; do
|
||||
read -p "$1 (y/n) " yn
|
||||
case $yn in
|
||||
[Yy]* ) break;;
|
||||
[Nn]* ) break;;
|
||||
* ) echo "Please answer yes or no. (y/n)";;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
lsblk
|
||||
echo ""
|
||||
echo "Please run the script with an existing mount path or the 'disk' device to format (e.g. sdx)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function stopscrypted() {
|
||||
cd $SCRYPTED_HOME
|
||||
echo ""
|
||||
echo "Stopping the Scrypted container. If there are any errors during disk setup, Scrypted will need to be manually restarted with:"
|
||||
echo "cd $SCRYPTED_HOME && docker compose up -d"
|
||||
echo ""
|
||||
sudo -u $SERVICE_USER docker compose down 2> /dev/null
|
||||
}
|
||||
|
||||
function removescryptedfstab() {
|
||||
backup "/etc/fstab"
|
||||
grep -v "scrypted-nvr" /etc/fstab > /tmp/fstab && cp /tmp/fstab /etc/fstab
|
||||
# ensure newline
|
||||
sed -i -e '$a\' /etc/fstab
|
||||
systemctl daemon-reload
|
||||
}
|
||||
|
||||
BLOCK_DEVICE="/dev/$1"
|
||||
if [ -b "$BLOCK_DEVICE" ]
|
||||
then
|
||||
readyn "Format $BLOCK_DEVICE?"
|
||||
if [ "$yn" == "n" ]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
stopscrypted
|
||||
|
||||
umount "$BLOCK_DEVICE"1 2> /dev/null
|
||||
umount "$BLOCK_DEVICE"2 2> /dev/null
|
||||
umount /mnt/scrypted-nvr 2> /dev/null
|
||||
|
||||
set -e
|
||||
parted "$BLOCK_DEVICE" --script mklabel gpt
|
||||
parted -a optimal "$BLOCK_DEVICE" mkpart scrypted-nvr "0%" "100%"
|
||||
set +e
|
||||
|
||||
sync
|
||||
PARTITION_DEVICE="$BLOCK_DEVICE"1
|
||||
if [ ! -e "$PARTITION_DEVICE" ]
|
||||
then
|
||||
PARTITION_DEVICE="$BLOCK_DEVICE"p1
|
||||
if [ ! -e "$PARTITION_DEVICE" ]
|
||||
then
|
||||
echo "Unable to determine block device partition from block device: $BLOCK_DEVICE"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
mkfs -F -t ext4 "$PARTITION_DEVICE"
|
||||
sync
|
||||
|
||||
# parse/evaluate blkid line as env vars
|
||||
for attr in $(blkid | grep "$BLOCK_DEVICE")
|
||||
do
|
||||
e=$(echo $attr | grep =)
|
||||
if [ ! -z "$e" ]
|
||||
then
|
||||
export "$e"
|
||||
fi
|
||||
done
|
||||
if [ -z "$UUID" ]
|
||||
then
|
||||
echo "Error parsing disk UUID."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "UUID: $UUID"
|
||||
set -e
|
||||
removescryptedfstab
|
||||
mkdir -p /mnt/scrypted-nvr
|
||||
echo "PARTLABEL=scrypted-nvr /mnt/scrypted-nvr ext4 defaults,nofail 0 0" >> /etc/fstab
|
||||
mount -a
|
||||
systemctl daemon-reload
|
||||
set +e
|
||||
|
||||
DIR="/mnt/scrypted-nvr"
|
||||
else
|
||||
if [ ! -d "$1" ]
|
||||
then
|
||||
echo "$1 is not a valid directory."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
stopscrypted
|
||||
|
||||
removescryptedfstab
|
||||
|
||||
DIR="$1"
|
||||
fi
|
||||
|
||||
ESCAPED_DIR=$(echo "$DIR" | sed s/\\//\\\\\\//g)
|
||||
|
||||
set -e
|
||||
sed -i s/'^.*:\/nvr'/" - $ESCAPED_DIR:\/nvr"/ "$DOCKER_COMPOSE_YML"
|
||||
sed -i s/'^.*SCRYPTED_NVR_VOLUME.*$'/" - SCRYPTED_NVR_VOLUME=\/nvr"/ "$DOCKER_COMPOSE_YML"
|
||||
set +e
|
||||
|
||||
cd $SCRYPTED_HOME
|
||||
sudo -u $SERVICE_USER docker compose up -d
|
||||
@@ -18,7 +18,6 @@ RUN add-apt-repository -y 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
|
||||
@@ -32,12 +31,12 @@ ENV SCRYPTED_CAN_RESTART="true"
|
||||
ENV SCRYPTED_VOLUME="/server/volume"
|
||||
ENV SCRYPTED_INSTALL_PATH="/server"
|
||||
|
||||
RUN test -f "/usr/bin/ffmpeg"
|
||||
RUN test -f "/usr/bin/ffmpeg" && test -f "/usr/bin/python3" && test -f "/usr/bin/python3.9" && test -f "/usr/bin/python3.10"
|
||||
ENV SCRYPTED_FFMPEG_PATH="/usr/bin/ffmpeg"
|
||||
ENV SCRYPTED_PYTHON_PATH="/usr/bin/python3"
|
||||
ENV SCRYPTED_PYTHON39_PATH="/usr/bin/python3.9"
|
||||
ENV SCRYPTED_PYTHON310_PATH="/usr/bin/python3.10"
|
||||
|
||||
# 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"
|
||||
|
||||
################################################################
|
||||
|
||||
@@ -21,7 +21,7 @@ RUN apt-get update && apt-get -y install \
|
||||
apt-get -y update && \
|
||||
apt-get -y upgrade
|
||||
|
||||
ARG NODE_VERSION=18
|
||||
ARG NODE_VERSION=20
|
||||
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
|
||||
@@ -57,11 +57,7 @@ RUN apt-get -y install \
|
||||
# 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
|
||||
|
||||
################################################################
|
||||
|
||||
@@ -89,8 +89,15 @@ USER_HOME=$(eval echo ~$SERVICE_USER)
|
||||
echo "Setting permissions on $USER_HOME/.scrypted"
|
||||
chown -R $SERVICE_USER $USER_HOME/.scrypted
|
||||
|
||||
echo "Stopping docker service if it exists..."
|
||||
cd $USER_HOME/.scrypted
|
||||
echo "docker compose down"
|
||||
sudo -u $SERVICE_USER docker compose down 2> /dev/null
|
||||
echo "docker compose rm -rf"
|
||||
sudo -u $SERVICE_USER docker rm -f /scrypted /scrypted-watchtower 2> /dev/null
|
||||
|
||||
echo "Installing Scrypted..."
|
||||
RUN sudo -u $SERVICE_USER npx -y scrypted@latest install-server
|
||||
RUN sudo -u $SERVICE_USER npx -y scrypted@latest install-server $SCRYPTED_INSTALL_VERSION
|
||||
|
||||
cat > /etc/systemd/system/scrypted.service <<EOT
|
||||
|
||||
@@ -103,10 +110,12 @@ User=$SERVICE_USER
|
||||
Group=$SERVICE_USER
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/npx -y scrypted serve
|
||||
Restart=on-failure
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
Environment="NODE_OPTIONS=$NODE_OPTIONS"
|
||||
Environment="SCRYPTED_INSTALL_ENVIRONMENT=$SCRYPTED_INSTALL_ENVIRONMENT"
|
||||
StandardOutput=null
|
||||
StandardError=null
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -39,7 +39,7 @@ launchctl unload ~/Library/LaunchAgents/app.scrypted.server.plist || echo ""
|
||||
echo "Installing Scrypted dependencies..."
|
||||
RUN_IGNORE xcode-select --install
|
||||
RUN brew update
|
||||
RUN_IGNORE brew install node@18
|
||||
RUN_IGNORE brew install node@20
|
||||
# snapshot plugin and others
|
||||
RUN brew install libvips
|
||||
# dlib
|
||||
@@ -81,17 +81,17 @@ echo "Installing Scrypted Launch Agent..."
|
||||
|
||||
RUN mkdir -p ~/Library/LaunchAgents
|
||||
|
||||
NODE_PATH=$(brew --prefix node@18)
|
||||
NODE_PATH=$(brew --prefix node@20)
|
||||
if [ ! -d "$NODE_PATH" ]
|
||||
then
|
||||
echo "Unable to determine node@18 path."
|
||||
echo "Unable to determine node@20 path."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NODE_BIN_PATH=$NODE_PATH/bin
|
||||
if [ ! -d "$NODE_BIN_PATH" ]
|
||||
then
|
||||
echo "Unable to determine node@18 bin path."
|
||||
echo "Unable to determine node@20 bin path."
|
||||
echo "$NODE_BIN_PATH does not exist."
|
||||
exit 1
|
||||
fi
|
||||
@@ -121,7 +121,7 @@ then
|
||||
fi
|
||||
|
||||
echo "Installing Scrypted..."
|
||||
RUN $NPX_PATH -y scrypted@latest install-server
|
||||
RUN $NPX_PATH -y scrypted@latest install-server $SCRYPTED_INSTALL_VERSION
|
||||
|
||||
cat > ~/Library/LaunchAgents/app.scrypted.server.plist <<EOT
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
@@ -8,8 +8,12 @@ sc.exe stop scrypted.exe
|
||||
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
|
||||
|
||||
# Install node.js
|
||||
choco upgrade -y nodejs-lts --version=18.14.0
|
||||
choco upgrade -y nodejs-lts --version=20.11.1
|
||||
|
||||
# Install VC Redist, which is necessary for portable python
|
||||
choco install -y vcredist140
|
||||
|
||||
# TODO: remove python install, and use portable python
|
||||
# Install Python
|
||||
choco upgrade -y python39
|
||||
# Run py.exe with a specific version
|
||||
@@ -22,7 +26,12 @@ $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";"
|
||||
py $SCRYPTED_WINDOWS_PYTHON_VERSION -m pip install --upgrade pip
|
||||
py $SCRYPTED_WINDOWS_PYTHON_VERSION -m pip install debugpy typing_extensions typing opencv-python
|
||||
|
||||
npx -y scrypted@latest install-server
|
||||
$SCRYPTED_INSTALL_VERSION=[System.Environment]::GetEnvironmentVariable("SCRYPTED_INSTALL_VERSION","User")
|
||||
if ($SCRYPTED_INSTALL_VERSION -eq $null) {
|
||||
npx -y scrypted@latest install-server
|
||||
} else {
|
||||
npx -y scrypted@latest install-server $SCRYPTED_INSTALL_VERSION
|
||||
}
|
||||
|
||||
$USER_HOME_ESCAPED = $env:USERPROFILE.replace('\', '\\')
|
||||
$SCRYPTED_HOME = $env:USERPROFILE + '\.scrypted'
|
||||
@@ -30,7 +39,8 @@ $SCRYPTED_HOME_ESCAPED_PATH = $SCRYPTED_HOME.replace('\', '\\')
|
||||
npm install --prefix $SCRYPTED_HOME @koush/node-windows --save
|
||||
|
||||
$NPX_PATH = (Get-Command npx).Path
|
||||
$NPX_PATH_ESCAPED = $NPX_PATH.replace('\', '\\')
|
||||
# The path needs double quotes to handle spaces in the directory path
|
||||
$NPX_PATH_ESCAPED = '"' + $NPX_PATH.replace('\', '\\') + '"'
|
||||
|
||||
$SERVICE_JS = @"
|
||||
const fs = require('fs');
|
||||
@@ -40,8 +50,10 @@ try {
|
||||
catch (e) {
|
||||
}
|
||||
const child_process = require('child_process');
|
||||
child_process.spawn('$($NPX_PATH_ESCAPED)', ['-y', 'scrypted', 'serve'], {
|
||||
child_process.spawn('$NPX_PATH_ESCAPED', ['-y', 'scrypted', 'serve'], {
|
||||
stdio: 'inherit',
|
||||
// allow spawning .cmd https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2
|
||||
shell: true,
|
||||
});
|
||||
"@
|
||||
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
cd /tmp
|
||||
curl -O -L https://github.com/koush/scrypted/releases/download/v0.72.0/scrypted.tar.zst
|
||||
pct restore 10443 scrypted.tar.zst
|
||||
|
||||
function readyn() {
|
||||
while true; do
|
||||
read -p "$1 (y/n) " yn
|
||||
@@ -13,6 +9,65 @@ function readyn() {
|
||||
done
|
||||
}
|
||||
|
||||
cd /tmp
|
||||
SCRYPTED_VERSION=v0.96.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" ]
|
||||
@@ -20,7 +75,10 @@ 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 can be started."
|
||||
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."
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
echo 'if (!process.version.startsWith("v18")) throw new Error("Node 18 is required. Install Node Version Manager (nvm) for versioned node installations. See https://github.com/koush/scrypted/pull/498#issuecomment-1373854020")' | node
|
||||
if [ "$?" != 0 ]
|
||||
then
|
||||
exit
|
||||
fi
|
||||
|
||||
#!/bin/bash
|
||||
echo ######################################
|
||||
echo "Setting up popular plugins."
|
||||
echo "Additional will need npm install manually."
|
||||
@@ -14,7 +9,7 @@ cd $(dirname $0)
|
||||
git submodule init
|
||||
git submodule update
|
||||
|
||||
for directory in sdk common server packages/client
|
||||
for directory in sdk server common 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, createHeadersArray, fetcher, getFetchMethod, hasHeader, setDefaultHttpFetchAccept, setHeader } 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 | undefined) => Promise<any>
|
||||
) {
|
||||
const authHttpFetch = async <T extends HttpFetchOptions<B>>(options: T & AuthFetchOptions): ReturnType<typeof h<T>> => {
|
||||
const method = getFetchMethod(options);
|
||||
const headers = createHeadersArray(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 && !hasHeader(headers, 'Authorization'))
|
||||
setHeader(headers, '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[] | null = 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)
|
||||
setHeader(headers, '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();
|
||||
17
packages/auth-fetch/tsconfig.json
Normal file
17
packages/auth-fetch/tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "esnext",
|
||||
"noImplicitAny": true,
|
||||
"outDir": "./dist",
|
||||
"esModuleInterop": true,
|
||||
"sourceMap": true,
|
||||
"inlineSources": true,
|
||||
"declaration": true,
|
||||
"resolveJsonModule": true,
|
||||
"strict": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
}
|
||||
2
packages/cli/.vscode/launch.json
vendored
2
packages/cli/.vscode/launch.json
vendored
@@ -21,7 +21,7 @@
|
||||
],
|
||||
"preLaunchTask": "npm: build",
|
||||
"args": [
|
||||
"shell",
|
||||
"login",
|
||||
],
|
||||
"sourceMaps": true,
|
||||
"resolveSourceMapLocations": [
|
||||
|
||||
40
packages/cli/package-lock.json
generated
40
packages/cli/package-lock.json
generated
@@ -1,17 +1,16 @@
|
||||
{
|
||||
"name": "scrypted",
|
||||
"version": "1.3.4",
|
||||
"version": "1.3.16",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "scrypted",
|
||||
"version": "1.3.4",
|
||||
"version": "1.3.16",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/client": "^1.3.2",
|
||||
"@scrypted/types": "^0.2.99",
|
||||
"axios": "^0.25.0",
|
||||
"@scrypted/client": "^1.3.3",
|
||||
"@scrypted/types": "^0.3.30",
|
||||
"engine.io-client": "^6.5.3",
|
||||
"readline-sync": "^1.4.10",
|
||||
"semver": "^7.5.4",
|
||||
@@ -92,20 +91,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/client": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/client/-/client-1.3.2.tgz",
|
||||
"integrity": "sha512-PZwjfKUYIMxBYm7V2o0/vAMlQmznKLN/d4rpshb5vV086mnhh578ik3h39awkwoPyWzNGDcYeoBY0BchhwtdOQ==",
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/client/-/client-1.3.3.tgz",
|
||||
"integrity": "sha512-Wuy7x02TCRy1buaDNX8NOIaL1j4ZXu4dqTTJsKHlPe3+umsBvpwbylD+YyyU8ghQJC6a40Bs5UMsvnCvNa/1fg==",
|
||||
"dependencies": {
|
||||
"@scrypted/types": "^0.2.99",
|
||||
"axios": "^0.25.0",
|
||||
"@scrypted/types": "^0.3.4",
|
||||
"engine.io-client": "^6.5.3",
|
||||
"follow-redirects": "^1.15.4",
|
||||
"rimraf": "^5.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/types": {
|
||||
"version": "0.2.99",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.2.99.tgz",
|
||||
"integrity": "sha512-2J1FH7tpAW5X3rgA70gJ+z0HFM90c/tBA+JXdP1vI1d/0yVmh9TSxnHoCuADN4R2NQXHmoZ6Nbds9kKAQ/25XQ=="
|
||||
"version": "0.3.30",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.3.30.tgz",
|
||||
"integrity": "sha512-1k+JVSR6WSNmE/5mLdqfrTmV3uRbvZp0OwKb8ikNi39ysBuC000tQGcEdXZqhYqRgWdhDTWtxXe9XsYoAZGKmA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.0",
|
||||
@@ -206,14 +206,6 @@
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz",
|
||||
"integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.7"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@@ -318,9 +310,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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "scrypted",
|
||||
"version": "1.3.5",
|
||||
"version": "1.3.16",
|
||||
"description": "",
|
||||
"main": "./dist/packages/cli/src/main.js",
|
||||
"bin": {
|
||||
@@ -16,19 +16,18 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/client": "^1.3.2",
|
||||
"@scrypted/types": "^0.2.99",
|
||||
"axios": "^0.25.0",
|
||||
"@scrypted/client": "^1.3.3",
|
||||
"@scrypted/types": "^0.3.30",
|
||||
"engine.io-client": "^6.5.3",
|
||||
"readline-sync": "^1.4.10",
|
||||
"semver": "^7.5.4",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"rimraf": "^5.0.5",
|
||||
"@types/node": "^20.9.4",
|
||||
"@types/readline-sync": "^1.4.8",
|
||||
"@types/semver": "^7.5.6",
|
||||
"rimraf": "^5.0.5",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.3.2"
|
||||
}
|
||||
|
||||
@@ -2,20 +2,15 @@
|
||||
|
||||
import { connectScryptedClient } from '@scrypted/client';
|
||||
import { FFmpegInput, ScryptedMimeTypes } from '@scrypted/types';
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
import child_process from 'child_process';
|
||||
import fs from 'fs';
|
||||
import https from 'https';
|
||||
import path from 'path';
|
||||
import readline from 'readline-sync';
|
||||
import semver from 'semver';
|
||||
import { httpFetch } from '../../../server/src/fetch/http-fetch';
|
||||
import { installServe, serveMain } from './service';
|
||||
import { connectShell } from './shell';
|
||||
|
||||
const httpsAgent = new https.Agent({
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
|
||||
if (!semver.gte(process.version, '16.0.0')) {
|
||||
throw new Error('"node" version out of date. Please update node to v16 or higher.')
|
||||
}
|
||||
@@ -45,6 +40,12 @@ interface LoginFile {
|
||||
[host: string]: Login;
|
||||
}
|
||||
|
||||
function basicAuthHeaders(username: string, password: string) {
|
||||
const headers = new Headers();
|
||||
headers.set('Authorization', `Basic ${Buffer.from(username + ":" + password).toString('base64')}`);
|
||||
return headers;
|
||||
}
|
||||
|
||||
async function doLogin(host: string) {
|
||||
host = toIpAndPort(host);
|
||||
|
||||
@@ -54,15 +55,15 @@ async function doLogin(host: string) {
|
||||
});
|
||||
|
||||
const url = `https://${host}/login`;
|
||||
const response = await axios(Object.assign({
|
||||
const response = await httpFetch({
|
||||
method: 'GET',
|
||||
auth: {
|
||||
username,
|
||||
password,
|
||||
},
|
||||
headers: basicAuthHeaders(username, password),
|
||||
url,
|
||||
httpsAgent,
|
||||
}, axiosConfig));
|
||||
rejectUnauthorized: false,
|
||||
responseType: 'json',
|
||||
});
|
||||
if (response.body.error)
|
||||
throw new Error(response.body.error);
|
||||
|
||||
fs.mkdirSync(scryptedHome, {
|
||||
recursive: true,
|
||||
@@ -78,15 +79,12 @@ async function doLogin(host: string) {
|
||||
login = {};
|
||||
login = login || {};
|
||||
|
||||
login[host] = response.data;
|
||||
login[host] = response.body;
|
||||
fs.writeFileSync(loginPath, JSON.stringify(login));
|
||||
return login;
|
||||
return login[host];
|
||||
}
|
||||
|
||||
async function getOrDoLogin(host: string): Promise<{
|
||||
username: string,
|
||||
token: string,
|
||||
}> {
|
||||
async function getOrDoLogin(host: string): Promise<Login> {
|
||||
let login: LoginFile;
|
||||
try {
|
||||
login = JSON.parse(fs.readFileSync(loginPath).toString());
|
||||
@@ -95,17 +93,12 @@ async function getOrDoLogin(host: string): Promise<{
|
||||
|
||||
if (!login[host].username || !login[host].token)
|
||||
throw new Error();
|
||||
|
||||
return login[host];
|
||||
}
|
||||
catch (e) {
|
||||
login = await doLogin(host);
|
||||
return doLogin(host);
|
||||
}
|
||||
return login[host];
|
||||
}
|
||||
|
||||
const axiosConfig: AxiosRequestConfig = {
|
||||
httpsAgent: new https.Agent({
|
||||
rejectUnauthorized: false
|
||||
})
|
||||
}
|
||||
|
||||
async function runCommand() {
|
||||
@@ -119,9 +112,6 @@ async function runCommand() {
|
||||
pluginId: '@scrypted/core',
|
||||
username: login.username,
|
||||
password: login.token,
|
||||
axiosConfig: {
|
||||
httpsAgent,
|
||||
}
|
||||
});
|
||||
|
||||
const device: any = sdk.systemManager.getDeviceById(idOrName) || sdk.systemManager.getDeviceByName(idOrName);
|
||||
@@ -158,8 +148,8 @@ async function main() {
|
||||
}
|
||||
else if (process.argv[2] === 'login') {
|
||||
const ip = process.argv[3] || '127.0.0.1';
|
||||
const token = await doLogin(ip);
|
||||
console.log('login successful. token:', token);
|
||||
const login = await doLogin(ip);
|
||||
console.log('login successful. token:', login.token);
|
||||
}
|
||||
else if (process.argv[2] === 'command') {
|
||||
const { sdk, pendingResult } = await runCommand();
|
||||
@@ -170,11 +160,11 @@ async function main() {
|
||||
const ffmpegInput = await sdk.mediaManager.convertMediaObjectToJSON<FFmpegInput>(await pendingResult, ScryptedMimeTypes.FFmpegInput);
|
||||
if (ffmpegInput.url && ffmpegInput.urls?.[0]) {
|
||||
const url = new URL(ffmpegInput.url);
|
||||
if (url.hostname === '127.0.0.1' && ffmpegInput.urls?.[0]) {
|
||||
ffmpegInput.inputArguments = ffmpegInput.inputArguments.map(i => i === ffmpegInput.url ? ffmpegInput.urls?.[0] : i);
|
||||
if (url.hostname === '127.0.0.1' && ffmpegInput.urls?.[0] && ffmpegInput.inputArguments) {
|
||||
ffmpegInput.inputArguments = ffmpegInput.inputArguments.map(i => i === ffmpegInput.url && ffmpegInput.urls ? ffmpegInput.urls?.[0] : i);
|
||||
}
|
||||
}
|
||||
const args = [...ffmpegInput.inputArguments];
|
||||
const args = ffmpegInput.inputArguments ? [...ffmpegInput.inputArguments] : [];
|
||||
if (ffmpegInput.h264FilterArguments)
|
||||
args.push(...ffmpegInput.h264FilterArguments);
|
||||
console.log('ffplay', ...args);
|
||||
@@ -211,16 +201,17 @@ async function main() {
|
||||
|
||||
const login = await getOrDoLogin(ip);
|
||||
const url = `https://${ip}/web/component/script/install/${pkg}`;
|
||||
const response = await axios(Object.assign({
|
||||
const response = await httpFetch({
|
||||
method: 'POST',
|
||||
auth: {
|
||||
username: login.username,
|
||||
password: login.token,
|
||||
},
|
||||
headers: basicAuthHeaders(login.username, login.token),
|
||||
url,
|
||||
}, axiosConfig));
|
||||
rejectUnauthorized: false,
|
||||
responseType: 'json',
|
||||
});
|
||||
if (response.body.error)
|
||||
throw new Error(response.body.error);
|
||||
|
||||
console.log('install successful. id:', response.data.id);
|
||||
console.log('install successful. id:', response.body.id);
|
||||
}
|
||||
else if (process.argv[2] === 'shell') {
|
||||
console.log = () => { };
|
||||
@@ -232,9 +223,6 @@ async function main() {
|
||||
pluginId: '@scrypted/core',
|
||||
username: login.username,
|
||||
password: login.token,
|
||||
axiosConfig: {
|
||||
httpsAgent,
|
||||
}
|
||||
});
|
||||
|
||||
const separator = process.argv.indexOf("--");
|
||||
@@ -258,7 +246,6 @@ async function main() {
|
||||
console.log(' npx scrypted install @scrypted/rtsp');
|
||||
console.log(' npx scrypted install @scrypted/rtsp/0.0.51');
|
||||
console.log(' npx scrypted install @scrypted/rtsp/0.0.51 192.168.2.100');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,12 @@ const EXIT_FILE = '.exit';
|
||||
const UPDATE_FILE = '.update';
|
||||
|
||||
async function runCommand(command: string, ...args: string[]) {
|
||||
if (os.platform() === 'win32')
|
||||
if (os.platform() === 'win32') {
|
||||
command += '.cmd';
|
||||
// wrap each argument in a quote to handle spaces in paths
|
||||
// https://github.com/nodejs/node/issues/38490#issuecomment-927330248
|
||||
args = args.map(arg => '"' + arg + '"');
|
||||
}
|
||||
console.log('running', command, ...args);
|
||||
const cp = child_process.spawn(command, args, {
|
||||
stdio: 'inherit',
|
||||
@@ -24,6 +28,8 @@ async function runCommand(command: string, ...args: string[]) {
|
||||
// https://github.com/lovell/sharp/blob/eefaa998725cf345227d94b40615e090495c6d09/lib/libvips.js#L115C19-L115C46
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS: 'true',
|
||||
},
|
||||
// allow spawning .cmd https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2
|
||||
shell: os.platform() === 'win32' ? true : undefined,
|
||||
});
|
||||
await once(cp, 'exit');
|
||||
if (cp.exitCode)
|
||||
@@ -84,7 +90,13 @@ export async function installServe(installVersion: string, ignoreError?: boolean
|
||||
const installJson = path.join(installDir, 'install.json');
|
||||
try {
|
||||
const { version } = JSON.parse(fs.readFileSync(installJson).toString());
|
||||
if (semver.parse(process.version).major !== semver.parse(version).major)
|
||||
const processSemver = semver.parse(process.version);
|
||||
if (!processSemver)
|
||||
throw new Error('error parsing process version');
|
||||
const installSemver = semver.parse(version);
|
||||
if (!installSemver)
|
||||
throw new Error('error parsing install.json version');
|
||||
if (processSemver.major !== installSemver.major)
|
||||
throw new Error('mismatch');
|
||||
}
|
||||
catch (e) {
|
||||
@@ -105,18 +117,38 @@ export async function installServe(installVersion: string, ignoreError?: boolean
|
||||
}
|
||||
|
||||
export async function serveMain(installVersion?: string) {
|
||||
let install = !!installVersion;
|
||||
const options = ((): { install: true; version: string } | { install: false } => {
|
||||
if (installVersion) {
|
||||
console.log(`Installing @scrypted/server@${installVersion}`);
|
||||
return {
|
||||
install: true,
|
||||
version: installVersion
|
||||
};
|
||||
}
|
||||
|
||||
if (!fs.existsSync('node_modules/@scrypted/server')) {
|
||||
console.log('Package @scrypted/server not found. Installing.');
|
||||
return {
|
||||
install: true,
|
||||
version: 'latest',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
install: false,
|
||||
}
|
||||
})();
|
||||
|
||||
const { installDir, volume } = cwdInstallDir();
|
||||
if (!fs.existsSync('node_modules/@scrypted/server')) {
|
||||
install = true;
|
||||
installVersion ||= 'latest';
|
||||
console.log('Package @scrypted/server not found. Installing.');
|
||||
}
|
||||
if (install) {
|
||||
await installServe(installVersion, true);
|
||||
|
||||
if (options.install) {
|
||||
await installServe(options.version, true);
|
||||
}
|
||||
|
||||
// todo: remove at some point after core lxc updater rolls out.
|
||||
if (process.env.SCRYPTED_INSTALL_ENVIRONMENT === 'lxc')
|
||||
process.env.SCRYPTED_FFMPEG_PATH = '/usr/bin/ffmpeg';
|
||||
|
||||
process.env.SCRYPTED_NPM_SERVE = 'true';
|
||||
process.env.SCRYPTED_VOLUME = volume;
|
||||
process.env.SCRYPTED_CAN_EXIT = 'true';
|
||||
@@ -129,16 +161,20 @@ export async function serveMain(installVersion?: string) {
|
||||
|
||||
await startServer(installDir);
|
||||
|
||||
if (fs.existsSync(EXIT_FILE)) {
|
||||
console.log('Exiting.');
|
||||
process.exit();
|
||||
}
|
||||
else if (fs.existsSync(UPDATE_FILE)) {
|
||||
if (fs.existsSync(UPDATE_FILE)) {
|
||||
console.log('Update requested. Installing.');
|
||||
await runCommandEatError('npm', '--prefix', installDir, 'install', '--production', '@scrypted/server@latest');
|
||||
await runCommandEatError('npm', '--prefix', installDir, 'install', '--production', '@scrypted/server@latest').catch(e => {
|
||||
console.error('Update failed', e);
|
||||
});
|
||||
console.log('Exiting.');
|
||||
process.exit(1);
|
||||
}
|
||||
else if (fs.existsSync(EXIT_FILE)) {
|
||||
console.log('Exiting.');
|
||||
process.exit(1);
|
||||
}
|
||||
else {
|
||||
console.log(`Service exited. Restarting momentarily.`);
|
||||
console.log(`Service unexpectedly exited. Restarting momentarily.`);
|
||||
await sleep(10000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"module": "Node16",
|
||||
"target": "esnext",
|
||||
"noImplicitAny": true,
|
||||
"outDir": "./dist",
|
||||
@@ -8,7 +8,8 @@
|
||||
"sourceMap": true,
|
||||
"inlineSources": true,
|
||||
"declaration": true,
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "Node16",
|
||||
"strict": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
|
||||
1
packages/client/.gitignore
vendored
1
packages/client/.gitignore
vendored
@@ -9,3 +9,4 @@ scrypted.db.bak
|
||||
.exit
|
||||
.update
|
||||
.venv
|
||||
.vscode/launch.json
|
||||
|
||||
27
packages/client/.vscode/launch.json
vendored
27
packages/client/.vscode/launch.json
vendored
@@ -1,27 +0,0 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ts-node",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"${relativeFile}"
|
||||
],
|
||||
"runtimeArgs": [
|
||||
"-r",
|
||||
"ts-node/register"
|
||||
],
|
||||
"env": {
|
||||
"SCRYPTED_USERNAME": "koush",
|
||||
"SCRYPTED_PASSWORD": "k9copUSA",
|
||||
},
|
||||
"cwd": "${workspaceRoot}",
|
||||
"protocol": "inspector",
|
||||
"internalConsoleOptions": "openOnSessionStart"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,21 +1,12 @@
|
||||
import { Camera, VideoCamera, VideoFrameGenerator } from '@scrypted/types';
|
||||
import { connectScryptedClient } from '../dist/packages/client/src';
|
||||
|
||||
import https from 'https';
|
||||
|
||||
const httpsAgent = new https.Agent({
|
||||
rejectUnauthorized: false,
|
||||
})
|
||||
|
||||
async function example() {
|
||||
const sdk = await connectScryptedClient({
|
||||
baseUrl: 'https://localhost:10443',
|
||||
pluginId: "@scrypted/core",
|
||||
username: process.env.SCRYPTED_USERNAME || 'admin',
|
||||
password: process.env.SCRYPTED_PASSWORD || 'swordfish',
|
||||
axiosConfig: {
|
||||
httpsAgent,
|
||||
}
|
||||
});
|
||||
console.log('server version', sdk.serverVersion);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
210
packages/client/package-lock.json
generated
210
packages/client/package-lock.json
generated
@@ -1,23 +1,36 @@
|
||||
{
|
||||
"name": "@scrypted/client",
|
||||
"version": "1.3.1",
|
||||
"version": "1.3.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/client",
|
||||
"version": "1.3.1",
|
||||
"version": "1.3.5",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/types": "^0.2.99",
|
||||
"axios": "^0.25.0",
|
||||
"@scrypted/types": "^0.3.27",
|
||||
"engine.io-client": "^6.5.3",
|
||||
"follow-redirects": "^1.15.6",
|
||||
"rimraf": "^5.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ip": "^1.1.3",
|
||||
"@types/node": "^20.9.4",
|
||||
"typescript": "^5.3.2"
|
||||
"@types/node": "^20.11.30",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.4.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
@@ -36,6 +49,31 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
|
||||
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
|
||||
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgjs/parseargs": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
@@ -46,15 +84,39 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/types": {
|
||||
"version": "0.2.99",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.2.99.tgz",
|
||||
"integrity": "sha512-2J1FH7tpAW5X3rgA70gJ+z0HFM90c/tBA+JXdP1vI1d/0yVmh9TSxnHoCuADN4R2NQXHmoZ6Nbds9kKAQ/25XQ=="
|
||||
"version": "0.3.27",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.3.27.tgz",
|
||||
"integrity": "sha512-XNtlqzqt6rHyNYwWrz3iiickh1h9ACwcLC3rfwxUbFk/Vq/UbDZgp0kGyj9UW6eLVNHzWFSE2dKqyyDS6V2KAg=="
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@tsconfig/node12": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@tsconfig/node14": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@tsconfig/node16": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/ip": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ip/-/ip-1.1.3.tgz",
|
||||
@@ -65,14 +127,35 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.4.tgz",
|
||||
"integrity": "sha512-wmyg8HUhcn6ACjsn8oKYjkN/zUzQeNtMy44weTJSM6p4MMzEOuKbA3OjJ267uPCOW7Xex9dyrNTful8XTQYoDA==",
|
||||
"version": "20.11.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
|
||||
"integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.3.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz",
|
||||
"integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
||||
@@ -95,13 +178,11 @@
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz",
|
||||
"integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.7"
|
||||
}
|
||||
"node_modules/arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
@@ -132,6 +213,12 @@
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"node_modules/create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@@ -161,6 +248,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
@@ -192,9 +288,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||
"version": "1.15.6",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@@ -284,6 +380,12 @@
|
||||
"node": "14 || >=16.14"
|
||||
}
|
||||
},
|
||||
"node_modules/make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
|
||||
@@ -469,10 +571,53 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "10.9.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
"@tsconfig/node10": "^1.0.7",
|
||||
"@tsconfig/node12": "^1.0.7",
|
||||
"@tsconfig/node14": "^1.0.0",
|
||||
"@tsconfig/node16": "^1.0.2",
|
||||
"acorn": "^8.4.1",
|
||||
"acorn-walk": "^8.1.1",
|
||||
"arg": "^4.1.0",
|
||||
"create-require": "^1.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"v8-compile-cache-lib": "^3.0.1",
|
||||
"yn": "3.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"ts-node": "dist/bin.js",
|
||||
"ts-node-cwd": "dist/bin-cwd.js",
|
||||
"ts-node-esm": "dist/bin-esm.js",
|
||||
"ts-node-script": "dist/bin-script.js",
|
||||
"ts-node-transpile-only": "dist/bin-transpile.js",
|
||||
"ts-script": "dist/bin-script-deprecated.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@swc/core": ">=1.2.50",
|
||||
"@swc/wasm": ">=1.2.50",
|
||||
"@types/node": "*",
|
||||
"typescript": ">=2.7"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@swc/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@swc/wasm": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz",
|
||||
"integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==",
|
||||
"version": "5.4.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
|
||||
"integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
@@ -488,6 +633,12 @@
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
@@ -613,6 +764,15 @@
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/client",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.5",
|
||||
"description": "",
|
||||
"main": "dist/packages/client/src/index.js",
|
||||
"scripts": {
|
||||
@@ -13,13 +13,14 @@
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/ip": "^1.1.3",
|
||||
"@types/node": "^20.9.4",
|
||||
"typescript": "^5.3.2"
|
||||
"@types/node": "^20.11.30",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@scrypted/types": "^0.2.99",
|
||||
"axios": "^0.25.0",
|
||||
"@scrypted/types": "^0.3.27",
|
||||
"engine.io-client": "^6.5.3",
|
||||
"follow-redirects": "^1.15.6",
|
||||
"rimraf": "^5.0.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,34 @@
|
||||
import { MediaObjectOptions, RTCConnectionManagement, RTCSignalingSession, ScryptedStatic } from "@scrypted/types";
|
||||
import axios, { AxiosRequestConfig, AxiosRequestHeaders } from 'axios';
|
||||
import { MediaObjectCreateOptions, RTCConnectionManagement, RTCSignalingSession, ScryptedStatic } from "@scrypted/types";
|
||||
import * as eio from 'engine.io-client';
|
||||
import { SocketOptions } from 'engine.io-client';
|
||||
import { Deferred } from "../../../common/src/deferred";
|
||||
import { timeoutPromise } from "../../../common/src/promise-utils";
|
||||
import { BrowserSignalingSession, waitPeerConnectionIceConnected, waitPeerIceConnectionClosed } from "../../../common/src/rtc-signaling";
|
||||
import { DataChannelDebouncer } from "../../../plugins/webrtc/src/datachannel-debouncer";
|
||||
import type { ClusterObject, ConnectRPCObject } from '../../../server/src/cluster/connect-rpc-object';
|
||||
import type { IOSocket } from '../../../server/src/io';
|
||||
import { MediaObject } from '../../../server/src/plugin/mediaobject';
|
||||
import { attachPluginRemote } from '../../../server/src/plugin/plugin-remote';
|
||||
import type { ClusterObject, ConnectRPCObject } from '../../../server/src/cluster/connect-rpc-object';
|
||||
import { RpcPeer } from '../../../server/src/rpc';
|
||||
import { createRpcDuplexSerializer, createRpcSerializer } from '../../../server/src/rpc-serializer';
|
||||
import packageJson from '../package.json';
|
||||
import { isIPAddress } from "./ip";
|
||||
|
||||
import { domFetch } from "../../../server/src/fetch";
|
||||
import { httpFetch } from '../../../server/src/fetch/http-fetch';
|
||||
|
||||
let fetcher: typeof httpFetch | typeof domFetch;
|
||||
try {
|
||||
if (process.arch === 'browser' as any)
|
||||
throw new Error();
|
||||
require('net');
|
||||
require('events');
|
||||
fetcher = httpFetch;
|
||||
}
|
||||
catch (e) {
|
||||
fetcher = domFetch;
|
||||
}
|
||||
|
||||
const sourcePeerId = RpcPeer.generateId();
|
||||
|
||||
type IOClientSocket = eio.Socket & IOSocket;
|
||||
@@ -59,7 +73,6 @@ export interface ScryptedConnectionOptions {
|
||||
local?: boolean;
|
||||
webrtc?: boolean;
|
||||
baseUrl?: string;
|
||||
axiosConfig?: AxiosRequestConfig;
|
||||
previousLoginResult?: ScryptedClientLoginResult;
|
||||
}
|
||||
|
||||
@@ -90,10 +103,13 @@ function isRunningStandalone() {
|
||||
|
||||
export async function logoutScryptedClient(baseUrl?: string) {
|
||||
const url = combineBaseUrl(baseUrl, 'logout');
|
||||
const response = await axios(url, {
|
||||
const response = await fetcher({
|
||||
url,
|
||||
withCredentials: true,
|
||||
responseType: 'json',
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
return response.data;
|
||||
return response.body;
|
||||
}
|
||||
|
||||
export function getCurrentBaseUrl() {
|
||||
@@ -122,38 +138,43 @@ export async function loginScryptedClient(options: ScryptedLoginOptions) {
|
||||
maxAge = 365 * 24 * 60 * 60 * 1000;
|
||||
|
||||
const url = combineBaseUrl(baseUrl, 'login');
|
||||
const response = await axios.post(url, {
|
||||
username,
|
||||
password,
|
||||
change_password,
|
||||
maxAge,
|
||||
}, {
|
||||
const response = await fetcher({
|
||||
url,
|
||||
body: {
|
||||
username,
|
||||
password,
|
||||
change_password,
|
||||
maxAge,
|
||||
},
|
||||
rejectUnauthorized: false,
|
||||
withCredentials: true,
|
||||
...options.axiosConfig,
|
||||
responseType: 'json',
|
||||
});
|
||||
|
||||
if (response.status !== 200)
|
||||
throw new Error('status ' + response.status);
|
||||
if (response.statusCode !== 200)
|
||||
throw new Error('status ' + response.statusCode);
|
||||
|
||||
const { body } = response;
|
||||
|
||||
return {
|
||||
error: response.data.error as string,
|
||||
authorization: response.data.authorization as string,
|
||||
queryToken: response.data.queryToken as any,
|
||||
token: response.data.token as string,
|
||||
addresses: response.data.addresses as string[],
|
||||
externalAddresses: response.data.externalAddresses as string[],
|
||||
error: body.error as string,
|
||||
authorization: body.authorization as string,
|
||||
queryToken: body.queryToken as any,
|
||||
token: body.token as string,
|
||||
addresses: body.addresses as string[],
|
||||
externalAddresses: body.externalAddresses as string[],
|
||||
// the cloud plugin will include this header.
|
||||
// should maybe move this into the cloud server itself.
|
||||
scryptedCloud: response.headers['x-scrypted-cloud'] === 'true',
|
||||
directAddress: response.headers['x-scrypted-direct-address'],
|
||||
cloudAddress: response.headers['x-scrypted-cloud-address'],
|
||||
scryptedCloud: response.headers.get('x-scrypted-cloud') === 'true',
|
||||
directAddress: response.headers.get('x-scrypted-direct-address'),
|
||||
cloudAddress: response.headers.get('x-scrypted-cloud-address'),
|
||||
};
|
||||
}
|
||||
|
||||
export async function checkScryptedClientLogin(options?: ScryptedConnectionOptions) {
|
||||
let { baseUrl } = options || {};
|
||||
let url = combineBaseUrl(baseUrl, 'login');
|
||||
const headers: AxiosRequestHeaders = {};
|
||||
const headers = new Headers();
|
||||
if (options?.previousLoginResult?.queryToken) {
|
||||
// headers.Authorization = options?.previousLoginResult?.authorization;
|
||||
// const search = new URLSearchParams(options.previousLoginResult.queryToken);
|
||||
@@ -161,32 +182,36 @@ export async function checkScryptedClientLogin(options?: ScryptedConnectionOptio
|
||||
const token = options?.previousLoginResult.username + ":" + options.previousLoginResult.token;
|
||||
const hash = Buffer.from(token).toString('base64');
|
||||
|
||||
headers.Authorization = `Basic ${hash}`;
|
||||
headers.set('Authorization', `Basic ${hash}`);
|
||||
}
|
||||
const response = await axios.get(url, {
|
||||
const response = await fetcher({
|
||||
url,
|
||||
withCredentials: true,
|
||||
headers,
|
||||
...options?.axiosConfig,
|
||||
rejectUnauthorized: false,
|
||||
responseType: 'json',
|
||||
});
|
||||
|
||||
const { body } = response;
|
||||
|
||||
return {
|
||||
baseUrl,
|
||||
hostname: response.data.hostname as string,
|
||||
redirect: response.data.redirect as string,
|
||||
username: response.data.username as string,
|
||||
expiration: response.data.expiration as number,
|
||||
hasLogin: !!response.data.hasLogin,
|
||||
error: response.data.error as string,
|
||||
authorization: response.data.authorization as string,
|
||||
queryToken: response.data.queryToken as any,
|
||||
token: response.data.token as string,
|
||||
addresses: response.data.addresses as string[],
|
||||
externalAddresses: response.data.externalAddresses as string[],
|
||||
hostname: body.hostname as string,
|
||||
redirect: body.redirect as string,
|
||||
username: body.username as string,
|
||||
expiration: body.expiration as number,
|
||||
hasLogin: !!body.hasLogin,
|
||||
error: body.error as string,
|
||||
authorization: body.authorization as string,
|
||||
queryToken: body.queryToken as any,
|
||||
token: body.token as string,
|
||||
addresses: body.addresses as string[],
|
||||
externalAddresses: body.externalAddresses as string[],
|
||||
// the cloud plugin will include this header.
|
||||
// should maybe move this into the cloud server itself.
|
||||
scryptedCloud: response.headers['x-scrypted-cloud'] === 'true',
|
||||
directAddress: response.headers['x-scrypted-direct-address'],
|
||||
cloudAddress: response.headers['x-scrypted-cloud-address'],
|
||||
scryptedCloud: response.headers.get('x-scrypted-cloud') === 'true',
|
||||
directAddress: response.headers.get('x-scrypted-direct-address'),
|
||||
cloudAddress: response.headers.get('x-scrypted-cloud-address'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -291,15 +316,9 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
}
|
||||
}
|
||||
|
||||
// the alternate urls must have a valid response.
|
||||
const loginCheckPromises = [...urlsToCheck].map(async baseUrl => {
|
||||
const loginCheck = await checkScryptedClientLogin({
|
||||
baseUrl,
|
||||
previousLoginResult: options?.previousLoginResult,
|
||||
});
|
||||
|
||||
function validateLoginResult(loginCheck: Awaited<ReturnType<typeof checkScryptedClientLogin>>) {
|
||||
if (loginCheck.error || loginCheck.redirect)
|
||||
throw new Error('login error');
|
||||
throw new ScryptedClientLoginError(loginCheck);
|
||||
|
||||
if (!loginCheck.authorization || !loginCheck.username || !loginCheck.queryToken) {
|
||||
console.error(loginCheck);
|
||||
@@ -307,11 +326,22 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
}
|
||||
|
||||
return loginCheck;
|
||||
}
|
||||
|
||||
// the alternate urls must have a valid response.
|
||||
const loginCheckPromises = [...urlsToCheck].map(baseUrl => {
|
||||
return checkScryptedClientLogin({
|
||||
baseUrl,
|
||||
previousLoginResult: options?.previousLoginResult,
|
||||
})
|
||||
.then(validateLoginResult);
|
||||
});
|
||||
|
||||
const baseUrlCheck = checkScryptedClientLogin({
|
||||
baseUrl,
|
||||
});
|
||||
previousLoginResult: options?.previousLoginResult,
|
||||
})
|
||||
.then(validateLoginResult);
|
||||
loginCheckPromises.push(baseUrlCheck);
|
||||
|
||||
let loginCheck: Awaited<ReturnType<typeof checkScryptedClientLogin>>;
|
||||
@@ -661,7 +691,7 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
} = scrypted;
|
||||
console.log('api attached', Date.now() - start);
|
||||
|
||||
mediaManager.createMediaObject = async<T extends MediaObjectOptions>(data: any, mimeType: string, options: T) => {
|
||||
mediaManager.createMediaObject = async<T extends MediaObjectCreateOptions>(data: any, mimeType: string, options: T) => {
|
||||
return new MediaObject(mimeType, data, options) as any;
|
||||
}
|
||||
|
||||
@@ -786,7 +816,7 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
return value;
|
||||
}
|
||||
|
||||
const { port, proxyId } = clusterObject;
|
||||
const { port, proxyId } = clusterObject;
|
||||
|
||||
// check if object is already connected
|
||||
const resolved = await resolveObject(proxyId, port);
|
||||
@@ -840,6 +870,8 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
cloudAddress,
|
||||
},
|
||||
connectRPCObject,
|
||||
fork: undefined,
|
||||
connect: undefined,
|
||||
}
|
||||
|
||||
socket.on('close', () => {
|
||||
|
||||
2
plugins/alexa/.vscode/settings.json
vendored
2
plugins/alexa/.vscode/settings.json
vendored
@@ -1,4 +1,4 @@
|
||||
|
||||
{
|
||||
"scrypted.debugHost": "koushik-ubuntu",
|
||||
"scrypted.debugHost": "127.0.0.1",
|
||||
}
|
||||
153
plugins/alexa/CHANGELOG.md
Normal file
153
plugins/alexa/CHANGELOG.md
Normal file
@@ -0,0 +1,153 @@
|
||||
<details>
|
||||
<summary>Changelog</summary>
|
||||
|
||||
### 0.3.1
|
||||
|
||||
alexa/google-home: fix potential vulnerability. do not allow local network control using cloud tokens belonging to a different user. the plugins are now locked to a specific scrypted cloud account once paired.
|
||||
|
||||
|
||||
### 0.3.0
|
||||
|
||||
alexa/google-home: additional auth token checks to harden endpoints for cloud sharing
|
||||
alexa: removed unneeded packages (#1319)
|
||||
alexa: added support for `light`, `outlet`, and `fan` device types (#1318)
|
||||
|
||||
|
||||
### 0.2.10
|
||||
|
||||
alexa: fix potential response race
|
||||
|
||||
|
||||
### 0.2.9
|
||||
|
||||
alexa: fix race condition in sendResponse
|
||||
|
||||
|
||||
### 0.2.8
|
||||
|
||||
alexa: display camera on doorbell press (#1066)
|
||||
|
||||
|
||||
### 0.2.7
|
||||
|
||||
alexa: added helpful error messages regarding token expiration (#1007)
|
||||
|
||||
|
||||
### 0.2.6
|
||||
|
||||
alexa: fix doorbells
|
||||
|
||||
|
||||
### 0.2.5
|
||||
|
||||
alexa: publish w/ storage fix
|
||||
|
||||
|
||||
### 0.2.4
|
||||
|
||||
alexa: add setting to publish debug events to console (#685)
|
||||
|
||||
|
||||
### 0.2.3
|
||||
|
||||
webrtc/alexa: add option to disable TURN on peers that already have externally reachable addresses
|
||||
|
||||
|
||||
### 0.2.1
|
||||
|
||||
alexa: set screen ratio to 720p (#625)
|
||||
|
||||
|
||||
### 0.2.0
|
||||
|
||||
alexa: refactor code structure (#606)
|
||||
|
||||
|
||||
### 0.1.0
|
||||
|
||||
alexa: ensure we are talking to the correct API endpoint (#580)
|
||||
|
||||
|
||||
### 0.0.20
|
||||
|
||||
alexa: provide hint that medium resolution is always used.
|
||||
|
||||
|
||||
### 0.0.19
|
||||
|
||||
various: minor cleanups
|
||||
alexa: added logging around `tokenInfo` resets (#488)
|
||||
sdk: rename sdk.version to sdk.serverVersion
|
||||
plugins: update tsconfig.json
|
||||
alexa: publish beta
|
||||
|
||||
|
||||
### 0.0.18
|
||||
|
||||
alexa: rethrow login failure error
|
||||
added support for type `Garage` and refactored the controller for future support (#479)
|
||||
updated install instructions (#478)
|
||||
webrtc/alexa: fix race condition with intercoms and track not received yet.
|
||||
|
||||
|
||||
### 0.0.17
|
||||
|
||||
alexa: close potential security hole if scrypted is exposed to the internet directly (ie, user is not using the cloud plugin against recommendations)
|
||||
|
||||
|
||||
### 0.0.16
|
||||
|
||||
plugins: remove postinstall
|
||||
plugins: add tsconfig.json
|
||||
alexa: doorbell motion sensor support
|
||||
|
||||
|
||||
### 0.0.15
|
||||
|
||||
alexa: fix harmless crash in log
|
||||
|
||||
|
||||
### 0.0.14
|
||||
|
||||
alexa: fix empty endpoint list
|
||||
|
||||
|
||||
### 0.0.13
|
||||
|
||||
all: prune package.json
|
||||
alexa: fix doorbell syncing
|
||||
|
||||
|
||||
### 0.0.12
|
||||
|
||||
alexa: publish
|
||||
|
||||
|
||||
### 0.0.10
|
||||
|
||||
alexa: 2 way audio
|
||||
|
||||
|
||||
### 0.0.4
|
||||
|
||||
alexa: 2 way audio
|
||||
alexa: motion events
|
||||
|
||||
|
||||
### 0.0.3
|
||||
|
||||
webrtc: refactor
|
||||
alexa: use rtc signaling channel
|
||||
alexa: publish
|
||||
|
||||
|
||||
### 0.0.1
|
||||
|
||||
alexa: doorbells
|
||||
alexa: sync devices properly
|
||||
alexa: add camera/doorbell, fix webrtc to work with amazon reqs
|
||||
alexa: initial pass with working cameras
|
||||
cloud: stub out alexa
|
||||
|
||||
|
||||
</details>
|
||||
39
plugins/alexa/package-lock.json
generated
39
plugins/alexa/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/alexa",
|
||||
"version": "0.2.10",
|
||||
"version": "0.3.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/alexa",
|
||||
"version": "0.2.10",
|
||||
"version": "0.3.2",
|
||||
"dependencies": {
|
||||
"axios": "^1.3.4",
|
||||
"uuid": "^9.0.0"
|
||||
@@ -14,33 +14,35 @@
|
||||
"devDependencies": {
|
||||
"@scrypted/common": "../../common",
|
||||
"@scrypted/sdk": "../../sdk",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/node": "^18.4.2"
|
||||
}
|
||||
},
|
||||
"../../common": {
|
||||
"name": "@scrypted/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"
|
||||
"http-auth-utils": "^5.0.1",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.9.0"
|
||||
"@types/node": "^20.11.0",
|
||||
"ts-node": "^10.9.2"
|
||||
}
|
||||
},
|
||||
"../../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.2.108",
|
||||
"version": "0.3.5",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"adm-zip": "^0.4.13",
|
||||
"axios": "^0.21.4",
|
||||
"axios": "^1.6.5",
|
||||
"babel-loader": "^9.1.0",
|
||||
"babel-plugin-const-enum": "^1.1.0",
|
||||
"esbuild": "^0.15.9",
|
||||
@@ -81,6 +83,21 @@
|
||||
"resolved": "../../sdk",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@types/debug": {
|
||||
"version": "4.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
||||
"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/ms": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ms": {
|
||||
"version": "0.7.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
|
||||
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.2.tgz",
|
||||
@@ -122,9 +139,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.5",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
|
||||
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"name": "@scrypted/alexa",
|
||||
"version": "0.2.10",
|
||||
"version": "0.3.2",
|
||||
"scripts": {
|
||||
"scrypted-setup-project": "scrypted-setup-project",
|
||||
"prescrypted-setup-project": "scrypted-package-json",
|
||||
"build": "scrypted-webpack",
|
||||
"prepublishOnly": "NODE_ENV=production scrypted-webpack",
|
||||
"prepublishOnly": "scrypted-changelog && NODE_ENV=production scrypted-webpack",
|
||||
"prescrypted-vscode-launch": "scrypted-webpack",
|
||||
"scrypted-vscode-launch": "scrypted-deploy-debug",
|
||||
"scrypted-deploy-debug": "scrypted-deploy-debug",
|
||||
"scrypted-debug": "scrypted-debug",
|
||||
"scrypted-deploy": "scrypted-deploy",
|
||||
"scrypted-readme": "scrypted-readme",
|
||||
"scrypted-package-json": "scrypted-package-json"
|
||||
"scrypted-changelog": "scrypted-changelog",
|
||||
"scrypted-package-json": "scrypted-package-json",
|
||||
"scrypted-readme": "scrypted-readme"
|
||||
},
|
||||
"keywords": [
|
||||
"scrypted",
|
||||
@@ -38,8 +39,9 @@
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.4.2",
|
||||
"@scrypted/common": "../../common",
|
||||
"@scrypted/sdk": "../../sdk",
|
||||
"@scrypted/common": "../../common"
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/node": "^18.4.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export declare type DisplayCategory = 'ACTIVITY_TRIGGER' | 'CAMERA' | 'CONTACT_SENSOR' | 'DOOR' | 'DOORBELL' | 'GARAGE_DOOR' | 'LIGHT' | 'MICROWAVE' | 'MOTION_SENSOR' | 'OTHER' | 'SCENE_TRIGGER' | 'SECURITY_PANEL' | 'SMARTLOCK' | 'SMARTPLUG' | 'SPEAKER' | 'SWITCH' | 'TEMPERATURE_SENSOR' | 'THERMOSTAT' | 'TV';
|
||||
export declare type DisplayCategory = 'ACTIVITY_TRIGGER' | 'CAMERA' | 'CONTACT_SENSOR' | 'DOOR' | 'DOORBELL' | 'GARAGE_DOOR' | 'LIGHT' | 'MICROWAVE' | 'MOTION_SENSOR' | 'OTHER' | 'SCENE_TRIGGER' | 'SECURITY_PANEL' | 'SMARTLOCK' | 'SMARTPLUG' | 'SPEAKER' | 'SWITCH' | 'TEMPERATURE_SENSOR' | 'THERMOSTAT' | 'TV' | 'FAN';
|
||||
|
||||
/*
|
||||
COMMON DIRECTIVES AND RESPONSES
|
||||
@@ -116,7 +116,7 @@ export interface ErrorPayload {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface ChangePayload {
|
||||
export interface ChangePayload extends Payload {
|
||||
change: {
|
||||
cause: {
|
||||
type: "APP_INTERACTION" | "PERIODIC_POLL" | "PHYSICAL_INTERACTION" | "VOICE_INTERACTION" | "RULE_TRIGGER";
|
||||
|
||||
@@ -27,6 +27,7 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
json: true
|
||||
},
|
||||
syncedDevices: {
|
||||
defaultValue: [],
|
||||
multiple: true,
|
||||
hide: true
|
||||
},
|
||||
@@ -47,7 +48,11 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
onPut(oldValue: boolean, newValue: boolean) {
|
||||
DEBUG = newValue;
|
||||
}
|
||||
}
|
||||
},
|
||||
pairedUserId: {
|
||||
title: "Pairing Key",
|
||||
description: "The pairing key used to validate requests from Alexa. Clear this key or delete the plugin to allow pairing with a different Alexa login.",
|
||||
},
|
||||
});
|
||||
|
||||
accessToken: Promise<string>;
|
||||
@@ -62,7 +67,10 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
alexaHandlers.set('Alexa.Authorization/AcceptGrant', this.onAlexaAuthorization);
|
||||
alexaHandlers.set('Alexa.Discovery/Discover', this.onDiscoverEndpoints);
|
||||
|
||||
this.start();
|
||||
this.start()
|
||||
.catch(e => {
|
||||
this.console.error('startup failed', e);
|
||||
})
|
||||
}
|
||||
|
||||
async start() {
|
||||
@@ -167,7 +175,7 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
}
|
||||
|
||||
if (!report) {
|
||||
this.console.warn(`${eventDetails.eventInterface}.${eventDetails.property} not supported for device ${eventSource.type}`);
|
||||
debug(`${eventDetails.eventInterface}.${eventDetails.property} not supported for device ${eventSource.type}`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -599,11 +607,22 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
try {
|
||||
debug("making authorization request to Scrypted");
|
||||
|
||||
await axios.get('https://home.scrypted.app/_punch/getcookie', {
|
||||
const getcookieResponse = await axios.get('https://home.scrypted.app/_punch/getcookie', {
|
||||
headers: {
|
||||
'Authorization': authorization,
|
||||
}
|
||||
});
|
||||
// new tokens will contain a lot of information, including the expiry and client id.
|
||||
// validate this. old tokens will be grandfathered in.
|
||||
if (getcookieResponse.data.expiry && getcookieResponse.data.clientId !== 'amazon')
|
||||
throw new Error('client id mismatch');
|
||||
if (!this.storageSettings.values.pairedUserId) {
|
||||
this.storageSettings.values.pairedUserId = getcookieResponse.data.id;
|
||||
}
|
||||
else if (this.storageSettings.values.pairedUserId !== getcookieResponse.data.id) {
|
||||
this.log.a('This plugin is already paired with a different account. Clear the existing key in the plugin settings to pair this plugin with a different account.');
|
||||
throw new Error('user id mismatch');
|
||||
}
|
||||
this.validAuths.add(authorization);
|
||||
}
|
||||
catch (e) {
|
||||
|
||||
71
plugins/alexa/src/types/fan.ts
Normal file
71
plugins/alexa/src/types/fan.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { OnOff, ScryptedDevice, ScryptedDeviceType, ScryptedInterface } from "@scrypted/sdk";
|
||||
import { DiscoveryEndpoint, ChangeReport, Report, Property, ChangePayload, DiscoveryCapability } from "../alexa";
|
||||
import { supportedTypes } from ".";
|
||||
|
||||
supportedTypes.set(ScryptedDeviceType.Fan, {
|
||||
async discover(device: ScryptedDevice): Promise<Partial<DiscoveryEndpoint>> {
|
||||
if (!device.interfaces.includes(ScryptedInterface.OnOff))
|
||||
return;
|
||||
|
||||
const capabilities: DiscoveryCapability[] = [];
|
||||
capabilities.push({
|
||||
"type": "AlexaInterface",
|
||||
"interface": "Alexa.PowerController",
|
||||
"version": "3",
|
||||
"properties": {
|
||||
"supported": [
|
||||
{
|
||||
"name": "powerState"
|
||||
}
|
||||
],
|
||||
"proactivelyReported": true,
|
||||
"retrievable": true
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
displayCategories: ['FAN'],
|
||||
capabilities
|
||||
}
|
||||
},
|
||||
async sendReport(eventSource: ScryptedDevice & OnOff): Promise<Partial<Report>> {
|
||||
return {
|
||||
context: {
|
||||
"properties": [
|
||||
{
|
||||
"namespace": "Alexa.PowerController",
|
||||
"name": "powerState",
|
||||
"value": eventSource.on ? "ON" : "OFF",
|
||||
"timeOfSample": new Date().toISOString(),
|
||||
"uncertaintyInMilliseconds": 0
|
||||
} as Property
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
async sendEvent(eventSource: ScryptedDevice & OnOff, eventDetails, eventData): Promise<Partial<Report>> {
|
||||
if (eventDetails.eventInterface !== ScryptedInterface.OnOff)
|
||||
return undefined;
|
||||
|
||||
return {
|
||||
event: {
|
||||
payload: {
|
||||
change: {
|
||||
cause: {
|
||||
type: "PHYSICAL_INTERACTION"
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
"namespace": "Alexa.PowerController",
|
||||
"name": "powerState",
|
||||
"value": eventData ? "ON" : "OFF",
|
||||
"timeOfSample": new Date().toISOString(),
|
||||
"uncertaintyInMilliseconds": 0
|
||||
} as Property
|
||||
]
|
||||
}
|
||||
} as ChangePayload,
|
||||
}
|
||||
} as Partial<ChangeReport>;
|
||||
}
|
||||
});
|
||||
@@ -13,8 +13,12 @@ export const supportedTypes = new Map<ScryptedDeviceType, SupportedType>();
|
||||
import '../handlers';
|
||||
import './camera';
|
||||
import './camera/handlers';
|
||||
import './light';
|
||||
import './light/handlers'
|
||||
import './fan';
|
||||
import './doorbell';
|
||||
import './garagedoor';
|
||||
import './outlet';
|
||||
import './switch';
|
||||
import './switch/handlers';
|
||||
import './sensor';
|
||||
|
||||
225
plugins/alexa/src/types/light.ts
Normal file
225
plugins/alexa/src/types/light.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
import { Brightness, ColorSettingHsv, ColorSettingTemperature, OnOff, ScryptedDevice, ScryptedDeviceType, ScryptedInterface } from "@scrypted/sdk";
|
||||
import { DiscoveryEndpoint, ChangeReport, Report, Property, ChangePayload, DiscoveryCapability, StateReport } from "../alexa";
|
||||
import { supportedTypes } from ".";
|
||||
|
||||
supportedTypes.set(ScryptedDeviceType.Light, {
|
||||
async discover(device: ScryptedDevice): Promise<Partial<DiscoveryEndpoint>> {
|
||||
if (!device.interfaces.includes(ScryptedInterface.OnOff))
|
||||
return;
|
||||
|
||||
const capabilities: DiscoveryCapability[] = [];
|
||||
if (device.interfaces.includes(ScryptedInterface.OnOff)) {
|
||||
capabilities.push({
|
||||
"type": "AlexaInterface",
|
||||
"interface": "Alexa.PowerController",
|
||||
"version": "3",
|
||||
"properties": {
|
||||
"supported": [{
|
||||
"name": "powerState"
|
||||
}],
|
||||
"proactivelyReported": true,
|
||||
"retrievable": true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (device.interfaces.includes(ScryptedInterface.Brightness)) {
|
||||
capabilities.push({
|
||||
"type": "AlexaInterface",
|
||||
"interface": "Alexa.BrightnessController",
|
||||
"version": "3",
|
||||
"properties": {
|
||||
"supported": [{
|
||||
"name": "brightness"
|
||||
}],
|
||||
"proactivelyReported": true,
|
||||
"retrievable": true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (device.interfaces.includes(ScryptedInterface.ColorSettingTemperature)) {
|
||||
capabilities.push({
|
||||
"type": "AlexaInterface",
|
||||
"interface": "Alexa.ColorTemperatureController",
|
||||
"version": "3",
|
||||
"properties": {
|
||||
"supported": [{
|
||||
"name": "colorTemperatureInKelvin"
|
||||
}],
|
||||
"proactivelyReported": true,
|
||||
"retrievable": true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (device.interfaces.includes(ScryptedInterface.ColorSettingHsv)) {
|
||||
capabilities.push({
|
||||
"type": "AlexaInterface",
|
||||
"interface": "Alexa.ColorController",
|
||||
"version": "3",
|
||||
"properties": {
|
||||
"supported": [{
|
||||
"name": "color"
|
||||
}],
|
||||
"proactivelyReported": true,
|
||||
"retrievable": true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
displayCategories: ['LIGHT'],
|
||||
capabilities
|
||||
}
|
||||
},
|
||||
async sendReport(eventSource: ScryptedDevice & OnOff & Brightness & ColorSettingHsv & ColorSettingTemperature): Promise<Partial<Report>> {
|
||||
let data = {
|
||||
context: {
|
||||
properties: []
|
||||
}
|
||||
|
||||
} as Partial<StateReport>;
|
||||
|
||||
if (eventSource.interfaces.includes(ScryptedInterface.OnOff)) {
|
||||
data.context.properties.push({
|
||||
"namespace": "Alexa.PowerController",
|
||||
"name": "powerState",
|
||||
"value": eventSource.on ? "ON" : "OFF",
|
||||
"timeOfSample": new Date().toISOString(),
|
||||
"uncertaintyInMilliseconds": 0
|
||||
});
|
||||
}
|
||||
|
||||
if (eventSource.interfaces.includes(ScryptedInterface.Brightness)) {
|
||||
data.context.properties.push({
|
||||
"namespace": "Alexa.BrightnessController",
|
||||
"name": "brightness",
|
||||
"value": eventSource.brightness,
|
||||
"timeOfSample": new Date().toISOString(),
|
||||
"uncertaintyInMilliseconds": 0
|
||||
});
|
||||
}
|
||||
|
||||
if (eventSource.interfaces.includes(ScryptedInterface.ColorSettingHsv) && eventSource.hsv) {
|
||||
data.context.properties.push({
|
||||
"namespace": "Alexa.ColorController",
|
||||
"name": "color",
|
||||
"value": {
|
||||
"hue": eventSource.hsv.h,
|
||||
"saturation": eventSource.hsv.s,
|
||||
"brightness": eventSource.hsv.v
|
||||
},
|
||||
"timeOfSample": new Date().toISOString(),
|
||||
"uncertaintyInMilliseconds": 0
|
||||
});
|
||||
}
|
||||
|
||||
if (eventSource.interfaces.includes(ScryptedInterface.ColorSettingTemperature) && eventSource.colorTemperature) {
|
||||
data.context.properties.push({
|
||||
"namespace": "Alexa.ColorTemperatureController",
|
||||
"name": "colorTemperatureInKelvin",
|
||||
"value": eventSource.colorTemperature,
|
||||
"timeOfSample": new Date().toISOString(),
|
||||
"uncertaintyInMilliseconds": 0
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
async sendEvent(eventSource: ScryptedDevice & OnOff & Brightness & ColorSettingHsv & ColorSettingTemperature, eventDetails, eventData): Promise<Partial<Report>> {
|
||||
if (eventDetails.eventInterface == ScryptedInterface.OnOff)
|
||||
return {
|
||||
event: {
|
||||
payload: {
|
||||
change: {
|
||||
cause: {
|
||||
type: "PHYSICAL_INTERACTION"
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
"namespace": "Alexa.PowerController",
|
||||
"name": "powerState",
|
||||
"value": eventData ? "ON" : "OFF",
|
||||
"timeOfSample": new Date(eventDetails.eventTime).toISOString(),
|
||||
"uncertaintyInMilliseconds": 0
|
||||
} as Property
|
||||
]
|
||||
}
|
||||
} as ChangePayload,
|
||||
}
|
||||
} as Partial<ChangeReport>;
|
||||
|
||||
if (eventDetails.eventInterface == ScryptedInterface.Brightness && eventSource.brightness)
|
||||
return {
|
||||
event: {
|
||||
payload: {
|
||||
change: {
|
||||
cause: {
|
||||
type: "PHYSICAL_INTERACTION"
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
"namespace": "Alexa.BrightnessController",
|
||||
"name": "brightness",
|
||||
"value": eventSource.brightness,
|
||||
"timeOfSample": new Date(eventDetails.eventTime).toISOString(),
|
||||
"uncertaintyInMilliseconds": 0
|
||||
} as Property
|
||||
]
|
||||
}
|
||||
} as ChangePayload,
|
||||
}
|
||||
} as Partial<ChangeReport>;
|
||||
|
||||
if (eventDetails.eventInterface == ScryptedInterface.ColorSettingHsv && eventSource.hsv)
|
||||
return {
|
||||
event: {
|
||||
payload: {
|
||||
change: {
|
||||
cause: {
|
||||
type: "PHYSICAL_INTERACTION"
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
"namespace": "Alexa.ColorController",
|
||||
"name": "color",
|
||||
"value": {
|
||||
"hue": eventSource.hsv.h,
|
||||
"saturation": eventSource.hsv.s,
|
||||
"brightness": eventSource.hsv.v
|
||||
},
|
||||
"timeOfSample": new Date(eventDetails.eventTime).toISOString(),
|
||||
"uncertaintyInMilliseconds": 0
|
||||
} as Property
|
||||
]
|
||||
}
|
||||
} as ChangePayload,
|
||||
}
|
||||
} as Partial<ChangeReport>;
|
||||
|
||||
if (eventDetails.eventInterface == ScryptedInterface.ColorSettingTemperature && eventSource.colorTemperature)
|
||||
return {
|
||||
event: {
|
||||
payload: {
|
||||
change: {
|
||||
cause: {
|
||||
type: "PHYSICAL_INTERACTION"
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
"namespace": "Alexa.ColorTemperatureController",
|
||||
"name": "colorTemperatureInKelvin",
|
||||
"value": eventSource.colorTemperature,
|
||||
"timeOfSample": new Date(eventDetails.eventTime).toISOString(),
|
||||
"uncertaintyInMilliseconds": 0
|
||||
} as Property
|
||||
]
|
||||
}
|
||||
} as ChangePayload,
|
||||
}
|
||||
} as Partial<ChangeReport>;
|
||||
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
166
plugins/alexa/src/types/light/handlers.ts
Normal file
166
plugins/alexa/src/types/light/handlers.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
import { Brightness, ColorHsv, ColorSettingHsv, ColorSettingTemperature, ScryptedDevice, ScryptedInterface } from "@scrypted/sdk";
|
||||
import { supportedTypes } from "..";
|
||||
import { deviceErrorResponse, sendDeviceResponse } from "../../common";
|
||||
import { v4 as createMessageId } from 'uuid';
|
||||
import { alexaDeviceHandlers } from "../../handlers";
|
||||
import { Directive, Response } from "../../alexa";
|
||||
import { error } from "console";
|
||||
|
||||
function commonBrightnessResponse(header, endpoint, payload, response, device: ScryptedDevice & Brightness) {
|
||||
const data : Response = {
|
||||
"event": {
|
||||
header,
|
||||
endpoint,
|
||||
payload
|
||||
},
|
||||
"context": {
|
||||
"properties": [
|
||||
{
|
||||
"namespace": "Alexa.PowerController",
|
||||
"name": "brightness",
|
||||
"value": device.brightness,
|
||||
"timeOfSample": new Date().toISOString(),
|
||||
"uncertaintyInMilliseconds": 500
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
data.event.header.namespace = "Alexa";
|
||||
data.event.header.name = "Response";
|
||||
data.event.header.messageId = createMessageId();
|
||||
|
||||
sendDeviceResponse(data, response, device);
|
||||
}
|
||||
|
||||
alexaDeviceHandlers.set('Alexa.BrightnessController/SetBrightness', async (request, response, directive: Directive, device: ScryptedDevice & Brightness) => {
|
||||
const supportedType = supportedTypes.get(device.type);
|
||||
if (!supportedType)
|
||||
return;
|
||||
|
||||
const { header, endpoint, payload } = directive;
|
||||
await device.setBrightness((payload as any).brightness)
|
||||
|
||||
commonBrightnessResponse(header, endpoint, payload, response, device);
|
||||
});
|
||||
|
||||
alexaDeviceHandlers.set('Alexa.BrightnessController/AdjustBrightness', async (request, response, directive: Directive, device: ScryptedDevice & Brightness) => {
|
||||
const supportedType = supportedTypes.get(device.type);
|
||||
if (!supportedType)
|
||||
return;
|
||||
|
||||
const { header, endpoint, payload } = directive;
|
||||
await device.setBrightness(device.brightness + (payload as any).brightnessDelta)
|
||||
|
||||
commonBrightnessResponse(header, endpoint, payload, response, device);
|
||||
});
|
||||
|
||||
alexaDeviceHandlers.set('Alexa.ColorController/SetColor', async (request, response, directive: Directive, device: ScryptedDevice & ColorSettingHsv) => {
|
||||
const supportedType = supportedTypes.get(device.type);
|
||||
if (!supportedType)
|
||||
return;
|
||||
|
||||
const { header, endpoint, payload } = directive;
|
||||
let hsv : ColorHsv = {
|
||||
h: (payload as any).color.hue,
|
||||
s: (payload as any).color.saturation,
|
||||
v: (payload as any).color.brightness
|
||||
};
|
||||
|
||||
if (!device.interfaces.includes(ScryptedInterface.ColorSettingHsv))
|
||||
return deviceErrorResponse("INVALID_REQUEST_EXCEPTION", "Device does not support setting color by HSV.", directive);
|
||||
|
||||
await device.setHsv(hsv.h, hsv.s, hsv.v);
|
||||
hsv = device.hsv;
|
||||
|
||||
const data : Response = {
|
||||
"event": {
|
||||
"header": {
|
||||
"namespace": "Alexa",
|
||||
"name": "Response",
|
||||
"messageId": createMessageId(),
|
||||
"correlationToken": header.correlationToken,
|
||||
"payloadVersion": header.payloadVersion
|
||||
},
|
||||
endpoint,
|
||||
payload
|
||||
},
|
||||
"context": {
|
||||
"properties": [
|
||||
{
|
||||
"namespace": "Alexa.ColorController",
|
||||
"name": "color",
|
||||
"value": {
|
||||
"hue": hsv.h,
|
||||
"saturation": hsv.s,
|
||||
"brightness": hsv.v
|
||||
},
|
||||
"timeOfSample": new Date().toISOString(),
|
||||
"uncertaintyInMilliseconds": 500
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
sendDeviceResponse(data, response, device);
|
||||
});
|
||||
|
||||
function commonColorTemperatureResponse(header, endpoint, payload, response, device: ScryptedDevice & ColorSettingTemperature) {
|
||||
const data : Response = {
|
||||
"event": {
|
||||
header,
|
||||
endpoint,
|
||||
payload
|
||||
},
|
||||
"context": {
|
||||
"properties": [
|
||||
{
|
||||
"namespace": "Alexa.ColorTemperatureController",
|
||||
"name": "colorTemperatureInKelvin",
|
||||
"value": device.colorTemperature,
|
||||
"timeOfSample": new Date().toISOString(),
|
||||
"uncertaintyInMilliseconds": 500
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
data.event.header.namespace = "Alexa";
|
||||
data.event.header.name = "Response";
|
||||
data.event.header.messageId = createMessageId();
|
||||
|
||||
sendDeviceResponse(data, response, device);
|
||||
}
|
||||
|
||||
alexaDeviceHandlers.set('Alexa.ColorTemperatureController/SetColorTemperature', async (request, response, directive: Directive, device: ScryptedDevice & ColorSettingTemperature) => {
|
||||
const supportedType = supportedTypes.get(device.type);
|
||||
if (!supportedType)
|
||||
return;
|
||||
|
||||
const { header, endpoint, payload } = directive;
|
||||
await device.setColorTemperature((payload as any).colorTemperatureInKelvin)
|
||||
|
||||
commonColorTemperatureResponse(header, endpoint, payload, response, device);
|
||||
});
|
||||
|
||||
alexaDeviceHandlers.set('Alexa.ColorTemperatureController/IncreaseColorTemperature', async (request, response, directive: Directive, device: ScryptedDevice & ColorSettingTemperature) => {
|
||||
const supportedType = supportedTypes.get(device.type);
|
||||
if (!supportedType)
|
||||
return;
|
||||
|
||||
const { header, endpoint, payload } = directive;
|
||||
await device.setColorTemperature(device.colorTemperature + 500);
|
||||
|
||||
commonColorTemperatureResponse(header, endpoint, payload, response, device);
|
||||
});
|
||||
|
||||
alexaDeviceHandlers.set('Alexa.ColorTemperatureController/DecreaseColorTemperature', async (request, response, directive: Directive, device: ScryptedDevice & ColorSettingTemperature) => {
|
||||
const supportedType = supportedTypes.get(device.type);
|
||||
if (!supportedType)
|
||||
return;
|
||||
|
||||
const { header, endpoint, payload } = directive;
|
||||
await device.setColorTemperature(device.colorTemperature - 500);
|
||||
|
||||
commonColorTemperatureResponse(header, endpoint, payload, response, device);
|
||||
});
|
||||
71
plugins/alexa/src/types/outlet.ts
Normal file
71
plugins/alexa/src/types/outlet.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { OnOff, ScryptedDevice, ScryptedDeviceType, ScryptedInterface } from "@scrypted/sdk";
|
||||
import { DiscoveryEndpoint, ChangeReport, Report, Property, ChangePayload, DiscoveryCapability } from "../alexa";
|
||||
import { supportedTypes } from ".";
|
||||
|
||||
supportedTypes.set(ScryptedDeviceType.Outlet, {
|
||||
async discover(device: ScryptedDevice): Promise<Partial<DiscoveryEndpoint>> {
|
||||
if (!device.interfaces.includes(ScryptedInterface.OnOff))
|
||||
return;
|
||||
|
||||
const capabilities: DiscoveryCapability[] = [];
|
||||
capabilities.push({
|
||||
"type": "AlexaInterface",
|
||||
"interface": "Alexa.PowerController",
|
||||
"version": "3",
|
||||
"properties": {
|
||||
"supported": [
|
||||
{
|
||||
"name": "powerState"
|
||||
}
|
||||
],
|
||||
"proactivelyReported": true,
|
||||
"retrievable": true
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
displayCategories: ['SMARTPLUG'],
|
||||
capabilities
|
||||
}
|
||||
},
|
||||
async sendReport(eventSource: ScryptedDevice & OnOff): Promise<Partial<Report>> {
|
||||
return {
|
||||
context: {
|
||||
"properties": [
|
||||
{
|
||||
"namespace": "Alexa.PowerController",
|
||||
"name": "powerState",
|
||||
"value": eventSource.on ? "ON" : "OFF",
|
||||
"timeOfSample": new Date().toISOString(),
|
||||
"uncertaintyInMilliseconds": 0
|
||||
} as Property
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
async sendEvent(eventSource: ScryptedDevice & OnOff, eventDetails, eventData): Promise<Partial<Report>> {
|
||||
if (eventDetails.eventInterface !== ScryptedInterface.OnOff)
|
||||
return undefined;
|
||||
|
||||
return {
|
||||
event: {
|
||||
payload: {
|
||||
change: {
|
||||
cause: {
|
||||
type: "PHYSICAL_INTERACTION"
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
"namespace": "Alexa.PowerController",
|
||||
"name": "powerState",
|
||||
"value": eventData ? "ON" : "OFF",
|
||||
"timeOfSample": new Date().toISOString(),
|
||||
"uncertaintyInMilliseconds": 0
|
||||
} as Property
|
||||
]
|
||||
}
|
||||
} as ChangePayload,
|
||||
}
|
||||
} as Partial<ChangeReport>;
|
||||
}
|
||||
});
|
||||
2
plugins/amcrest/.vscode/launch.json
vendored
2
plugins/amcrest/.vscode/launch.json
vendored
@@ -10,7 +10,7 @@
|
||||
"port": 10081,
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"**/plugin-remote-worker.*",
|
||||
"**/plugin-console.*",
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"preLaunchTask": "scrypted: deploy+debug",
|
||||
|
||||
@@ -39,6 +39,8 @@ Each 'Channel' or (camera) Device attached to the NVR must be configured as sepa
|
||||
* `Snapshot URL Override` camera's IP address (preferred) or specific port number of NVR for that camera (may work). That is: `http://<camera IP address>/cgi-bin/snapshot.cgi` or `http://<NVR IP address>:<NVR port # for camera>/cgi-bin/snapshot.cgi`
|
||||
* `Channel Number Override` camera's channel number as known to DVR
|
||||
|
||||
## Dahua Lock/Unlock
|
||||
Dahua DTO video intercoms have built-in access control for locks/doors. If you have set the Amcrest plugin up with `Doorbell Type` set to `Dahua Doorbell`, you can enable support for remotely locking/unlocking by enabling/toggle the option `Enable Dahua Lock`.
|
||||
|
||||
# Troubleshooting
|
||||
## General
|
||||
|
||||
186
plugins/amcrest/package-lock.json
generated
186
plugins/amcrest/package-lock.json
generated
@@ -1,22 +1,21 @@
|
||||
{
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.130",
|
||||
"version": "0.0.151",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.130",
|
||||
"version": "0.0.151",
|
||||
"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"
|
||||
"content-type": "^1.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.16.18"
|
||||
"@types/content-type": "^1.1.8",
|
||||
"@types/node": "^20.11.30"
|
||||
}
|
||||
},
|
||||
"../../common": {
|
||||
@@ -26,22 +25,22 @@
|
||||
"dependencies": {
|
||||
"@scrypted/sdk": "file:../sdk",
|
||||
"@scrypted/server": "file:../server",
|
||||
"http-auth-utils": "^3.0.2",
|
||||
"node-fetch-commonjs": "^3.1.1",
|
||||
"typescript": "^4.4.3"
|
||||
"http-auth-utils": "^5.0.1",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.9.0"
|
||||
"@types/node": "^20.11.0",
|
||||
"ts-node": "^10.9.2"
|
||||
}
|
||||
},
|
||||
"../../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.3.2",
|
||||
"version": "0.3.29",
|
||||
"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",
|
||||
@@ -71,15 +70,6 @@
|
||||
"typedoc": "^0.23.21"
|
||||
}
|
||||
},
|
||||
"node_modules/@koush/axios-digest-auth": {
|
||||
"version": "0.8.6",
|
||||
"resolved": "https://registry.npmjs.org/@koush/axios-digest-auth/-/axios-digest-auth-0.8.6.tgz",
|
||||
"integrity": "sha512-e/XKs7/BYpPQkces0Cm4dUmhT9hR0rjvnNZAVRyRnNWdQ8cyCMFWS9HIrMWOdzAocKDNBXi1vKjJ8CywrW5xgQ==",
|
||||
"dependencies": {
|
||||
"auth-header": "^1.0.0",
|
||||
"axios": "^0.21.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/common": {
|
||||
"resolved": "../../common",
|
||||
"link": true
|
||||
@@ -88,150 +78,34 @@
|
||||
"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/content-type": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/content-type/-/content-type-1.1.8.tgz",
|
||||
"integrity": "sha512-1tBhmVUeso3+ahfyaKluXe38p+94lovUZdoVfQ3OnJo9uJC42JT7CBoN3k9HYhAae+GwiBYmHu+N9FZhOG+2Pg==",
|
||||
"dev": true
|
||||
},
|
||||
"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.11.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
|
||||
"integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
|
||||
"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==",
|
||||
"node_modules/content-type": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||
"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.130",
|
||||
"version": "0.0.151",
|
||||
"description": "Amcrest Plugin for Scrypted",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache",
|
||||
@@ -35,13 +35,12 @@
|
||||
]
|
||||
},
|
||||
"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"
|
||||
"content-type": "^1.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.16.18"
|
||||
"@types/content-type": "^1.1.8",
|
||||
"@types/node": "^20.11.30"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,140 @@
|
||||
import AxiosDigestAuth from '@koush/axios-digest-auth';
|
||||
import { Readable } from 'stream';
|
||||
import https from 'https';
|
||||
import { AuthFetchCredentialState, HttpFetchOptions, authHttpFetch } from '@scrypted/common/src/http-auth-fetch';
|
||||
import { readLine } from '@scrypted/common/src/read-stream';
|
||||
import { parseHeaders, readBody } from '@scrypted/common/src/rtsp-server';
|
||||
import contentType from 'content-type';
|
||||
import { IncomingMessage } from 'http';
|
||||
import { amcrestHttpsAgent, getDeviceInfo } from './probe';
|
||||
import { EventEmitter, Readable } from 'stream';
|
||||
import { Destroyable } from '../../rtsp/src/rtsp';
|
||||
import { getDeviceInfo } from './probe';
|
||||
import { Point } from '@scrypted/sdk';
|
||||
|
||||
// Human
|
||||
// {
|
||||
// "Action" : "Cross",
|
||||
// "Class" : "Normal",
|
||||
// "CountInGroup" : 1,
|
||||
// "DetectRegion" : [
|
||||
// [ 455, 260 ],
|
||||
// [ 3586, 260 ],
|
||||
// [ 3768, 7580 ],
|
||||
// [ 382, 7451 ]
|
||||
// ],
|
||||
// "Direction" : "Enter",
|
||||
// "EventID" : 10181,
|
||||
// "GroupID" : 0,
|
||||
// "Name" : "Rule1",
|
||||
// "Object" : {
|
||||
// "Action" : "Appear",
|
||||
// "BoundingBox" : [ 2856, 1280, 3880, 4880 ],
|
||||
// "Center" : [ 3368, 3080 ],
|
||||
// "Confidence" : 0,
|
||||
// "LowerBodyColor" : [ 0, 0, 0, 0 ],
|
||||
// "MainColor" : [ 0, 0, 0, 0 ],
|
||||
// "ObjectID" : 863,
|
||||
// "ObjectType" : "Human",
|
||||
// "RelativeID" : 0,
|
||||
// "Speed" : 0
|
||||
// },
|
||||
// "PTS" : 43380319830.0,
|
||||
// "RuleID" : 2,
|
||||
// "Track" : [],
|
||||
// "UTC" : 1711446999,
|
||||
// "UTCMS" : 701
|
||||
// }
|
||||
|
||||
// Face
|
||||
// {
|
||||
// "CfgRuleId" : 1,
|
||||
// "Class" : "FaceDetection",
|
||||
// "CountInGroup" : 2,
|
||||
// "DetectRegion" : null,
|
||||
// "EventID" : 10360,
|
||||
// "EventSeq" : 6,
|
||||
// "Faces" : [
|
||||
// {
|
||||
// "BoundingBox" : [ 1504, 2336, 1728, 2704 ],
|
||||
// "Center" : [ 1616, 2520 ],
|
||||
// "ObjectID" : 94,
|
||||
// "ObjectType" : "HumanFace",
|
||||
// "RelativeID" : 0
|
||||
// }
|
||||
// ],
|
||||
// "FrameSequence" : 8251212,
|
||||
// "GroupID" : 6,
|
||||
// "Mark" : 0,
|
||||
// "Name" : "FaceDetection",
|
||||
// "Object" : {
|
||||
// "Action" : "Appear",
|
||||
// "BoundingBox" : [ 1504, 2336, 1728, 2704 ],
|
||||
// "Center" : [ 1616, 2520 ],
|
||||
// "Confidence" : 19,
|
||||
// "FrameSequence" : 8251212,
|
||||
// "ObjectID" : 94,
|
||||
// "ObjectType" : "HumanFace",
|
||||
// "RelativeID" : 0,
|
||||
// "SerialUUID" : "",
|
||||
// "Source" : 0.0,
|
||||
// "Speed" : 0,
|
||||
// "SpeedTypeInternal" : 0
|
||||
// },
|
||||
// "Objects" : [
|
||||
// {
|
||||
// "Action" : "Appear",
|
||||
// "BoundingBox" : [ 1504, 2336, 1728, 2704 ],
|
||||
// "Center" : [ 1616, 2520 ],
|
||||
// "Confidence" : 19,
|
||||
// "FrameSequence" : 8251212,
|
||||
// "ObjectID" : 94,
|
||||
// "ObjectType" : "HumanFace",
|
||||
// "RelativeID" : 0,
|
||||
// "SerialUUID" : "",
|
||||
// "Source" : 0.0,
|
||||
// "Speed" : 0,
|
||||
// "SpeedTypeInternal" : 0
|
||||
// }
|
||||
// ],
|
||||
// "PTS" : 43774941350.0,
|
||||
// "Priority" : 0,
|
||||
// "RuleID" : 1,
|
||||
// "RuleId" : 1,
|
||||
// "Source" : -1280470024.0,
|
||||
// "UTC" : 947510337,
|
||||
// "UTCMS" : 0
|
||||
// }
|
||||
export interface AmcrestObjectDetails {
|
||||
Action: string;
|
||||
BoundingBox: Point;
|
||||
Center: Point;
|
||||
Confidence: number;
|
||||
LowerBodyColor: [number, number, number, number];
|
||||
MainColor: [number, number, number, number];
|
||||
ObjectID: number;
|
||||
ObjectType: string;
|
||||
RelativeID: number;
|
||||
Speed: number;
|
||||
}
|
||||
|
||||
export interface AmcrestEventData {
|
||||
Action: string;
|
||||
Class: string;
|
||||
CountInGroup: number;
|
||||
DetectRegion: Point[];
|
||||
Direction: string;
|
||||
EventID: number;
|
||||
GroupID: number;
|
||||
Name: string;
|
||||
Object: AmcrestObjectDetails;
|
||||
PTS: number;
|
||||
RuleID: number;
|
||||
Track: any[];
|
||||
UTC: number;
|
||||
UTCMS: number;
|
||||
}
|
||||
|
||||
export enum AmcrestEvent {
|
||||
MotionStart = "Code=VideoMotion;action=Start",
|
||||
MotionStop = "Code=VideoMotion;action=Stop",
|
||||
MotionInfo = "Code=VideoMotionInfo;action=State",
|
||||
AudioStart = "Code=AudioMutation;action=Start",
|
||||
AudioStop = "Code=AudioMutation;action=Stop",
|
||||
TalkInvite = "Code=_DoTalkAction_;action=Invite",
|
||||
@@ -20,37 +148,69 @@ export enum AmcrestEvent {
|
||||
DahuaTalkHangup = "Code=PassiveHungup;action=Start",
|
||||
DahuaCallDeny = "Code=HungupPhone;action=Pulse",
|
||||
DahuaTalkPulse = "Code=_CallNoAnswer_;action=Pulse",
|
||||
FaceDetection = "Code=FaceDetection;action=Start",
|
||||
SmartMotionHuman = "Code=SmartMotionHuman;action=Start",
|
||||
SmartMotionVehicle = "Code=Vehicle;action=Start",
|
||||
CrossLineDetection = "Code=CrossLineDetection;action=Start",
|
||||
CrossRegionDetection = "Code=CrossRegionDetection;action=Start",
|
||||
}
|
||||
|
||||
|
||||
async function readAmcrestMessage(client: Readable): Promise<string[]> {
|
||||
let currentHeaders: string[] = [];
|
||||
while (true) {
|
||||
const originalLine = await readLine(client);
|
||||
const line = originalLine.trim();
|
||||
if (!line)
|
||||
return currentHeaders;
|
||||
// dahua bugs out and sends message without a newline separating the body:
|
||||
// Content-Length:39
|
||||
// Code=AudioMutation;action=Start;index=0
|
||||
if (!line.includes(':')) {
|
||||
client.unshift(Buffer.from(originalLine + '\n'));
|
||||
return currentHeaders;
|
||||
}
|
||||
currentHeaders.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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,69 +221,167 @@ 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',
|
||||
async jpegSnapshot(timeout = 10000): Promise<Buffer> {
|
||||
const response = await this.request({
|
||||
url: `http://${this.ip}/cgi-bin/snapshot.cgi`,
|
||||
timeout: 60000,
|
||||
timeout,
|
||||
});
|
||||
|
||||
return Buffer.from(response.data);
|
||||
return response.body;
|
||||
}
|
||||
|
||||
async listenEvents() {
|
||||
async listenEvents(): Promise<Destroyable> {
|
||||
const events = new EventEmitter();
|
||||
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: IncomingMessage = response.body;
|
||||
(events as any).destroy = () => {
|
||||
stream.destroy();
|
||||
events.removeAllListeners();
|
||||
};
|
||||
stream.on('close', () => {
|
||||
events.emit('close');
|
||||
});
|
||||
stream.on('end', () => {
|
||||
events.emit('end');
|
||||
});
|
||||
stream.on('error', e => {
|
||||
events.emit('error', e);
|
||||
});
|
||||
const stream = response.data as IncomingMessage;
|
||||
stream.socket.setKeepAlive(true);
|
||||
|
||||
stream.on('data', (buffer: Buffer) => {
|
||||
const data = buffer.toString();
|
||||
const parts = data.split(';');
|
||||
let index: string;
|
||||
try {
|
||||
for (const part of parts) {
|
||||
if (part.startsWith('index')) {
|
||||
index = part.split('=')[1]?.trim();
|
||||
|
||||
const ct = stream.headers['content-type'];
|
||||
// make content type parsable as content disposition filename
|
||||
const cd = contentType.parse(ct);
|
||||
let { boundary } = cd.parameters;
|
||||
// amcrest may send "--myboundary" or "-- myboundary" (with a space)
|
||||
const altBoundary = `-- ${boundary}`;
|
||||
boundary = `--${boundary}`;
|
||||
const boundaryEnd = `${boundary}--`;
|
||||
|
||||
|
||||
(async () => {
|
||||
while (true) {
|
||||
let ignore = await readLine(stream);
|
||||
ignore = ignore.trim();
|
||||
if (!ignore)
|
||||
continue;
|
||||
if (ignore === boundaryEnd)
|
||||
continue;
|
||||
// dahua bugs out and sends this.
|
||||
if (ignore === 'HTTP/1.1 200 OK') {
|
||||
const message = await readAmcrestMessage(stream);
|
||||
this.console.log('ignoring dahua http message', message);
|
||||
message.unshift('');
|
||||
const headers = parseHeaders(message);
|
||||
const body = await readBody(stream, headers);
|
||||
if (body)
|
||||
this.console.log('ignoring dahua http body', body);
|
||||
continue;
|
||||
}
|
||||
if (ignore !== boundary && ignore !== altBoundary) {
|
||||
this.console.error('expected boundary but found', ignore);
|
||||
this.console.error(response.headers);
|
||||
throw new Error('expected boundary');
|
||||
}
|
||||
|
||||
const message = await readAmcrestMessage(stream);
|
||||
events.emit('data', message);
|
||||
message.unshift('');
|
||||
const headers = parseHeaders(message);
|
||||
const body = await readBody(stream, headers);
|
||||
|
||||
const data = body.toString();
|
||||
events.emit('data', data);
|
||||
|
||||
const parts = data.split(';');
|
||||
let index: string;
|
||||
try {
|
||||
for (const part of parts) {
|
||||
if (part.startsWith('index')) {
|
||||
index = part.split('=')[1]?.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
this.console.error('error parsing index', data);
|
||||
}
|
||||
let jsonData: any;
|
||||
try {
|
||||
for (const part of parts) {
|
||||
if (part.startsWith('data')) {
|
||||
jsonData = JSON.parse(part.split('=')[1]?.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
this.console.error('error parsing data', data);
|
||||
}
|
||||
|
||||
for (const event of Object.values(AmcrestEvent)) {
|
||||
if (data.indexOf(event) !== -1) {
|
||||
events.emit('event', event, index, data);
|
||||
|
||||
if (event === AmcrestEvent.SmartMotionHuman) {
|
||||
events.emit('smart', 'person', jsonData);
|
||||
}
|
||||
else if (event === AmcrestEvent.SmartMotionVehicle) {
|
||||
events.emit('smart', 'car', jsonData);
|
||||
}
|
||||
else if (event === AmcrestEvent.FaceDetection) {
|
||||
events.emit('smart', 'face', jsonData);
|
||||
}
|
||||
else if (event === AmcrestEvent.CrossLineDetection || event === AmcrestEvent.CrossRegionDetection) {
|
||||
const eventData: AmcrestEventData = jsonData;
|
||||
if (eventData?.Object?.ObjectType === 'Human') {
|
||||
events.emit('smart', 'person', eventData);
|
||||
}
|
||||
else if (eventData?.Object?.ObjectType === 'Vehicle') {
|
||||
events.emit('smart', 'car', eventData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
this.console.error('error parsing index', data);
|
||||
}
|
||||
// this.console?.log('event', data);
|
||||
for (const event of Object.values(AmcrestEvent)) {
|
||||
if (data.indexOf(event) !== -1) {
|
||||
stream.emit('event', event, index, data);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return stream;
|
||||
})()
|
||||
.catch(() => stream.destroy());
|
||||
return events as any as Destroyable;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
async unlock(): Promise<boolean> {
|
||||
const response = await this.request({
|
||||
url: `http://${this.ip}/cgi-bin/accessControl.cgi?action=openDoor&channel=1&UserID=101&Type=Remote`,
|
||||
responseType: 'text',
|
||||
});
|
||||
return response.body.includes('OK');
|
||||
}
|
||||
|
||||
async lock(): Promise<boolean> {
|
||||
const response = await this.request({
|
||||
url: `http://${this.ip}/cgi-bin/accessControl.cgi?action=closeDoor&channel=1&UserID=101&Type=Remote`,
|
||||
responseType: 'text',
|
||||
});
|
||||
return response.body.includes('OK');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { ffmpegLogInitialOutput } from '@scrypted/common/src/media-helpers';
|
||||
import { readLength } from "@scrypted/common/src/read-stream";
|
||||
import sdk, { Camera, DeviceCreatorSettings, DeviceInformation, FFmpegInput, Intercom, MediaObject, MediaStreamOptions, PictureOptions, Reboot, RequestRecordingStreamOptions, ResponseMediaStreamOptions, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, VideoCameraConfiguration, VideoRecorder } from "@scrypted/sdk";
|
||||
import sdk, { Camera, DeviceCreatorSettings, DeviceInformation, FFmpegInput, Intercom, Lock, MediaObject, MediaStreamOptions, ObjectDetectionTypes, ObjectDetector, ObjectsDetected, Reboot, RequestPictureOptions, RequestRecordingStreamOptions, ResponseMediaStreamOptions, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, VideoCameraConfiguration, VideoRecorder } from "@scrypted/sdk";
|
||||
import child_process, { ChildProcess } from 'child_process';
|
||||
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';
|
||||
import { AmcrestCameraClient, AmcrestEvent, AmcrestEventData } from "./amcrest-api";
|
||||
|
||||
const { mediaManager } = sdk;
|
||||
|
||||
@@ -23,12 +22,13 @@ function findValue(blob: string, prefix: string, key: string) {
|
||||
return parts[1];
|
||||
}
|
||||
|
||||
class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration, Camera, Intercom, VideoRecorder, Reboot {
|
||||
class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration, Camera, Intercom, Lock, VideoRecorder, Reboot, ObjectDetector {
|
||||
eventStream: Stream;
|
||||
cp: ChildProcess;
|
||||
client: AmcrestCameraClient;
|
||||
videoStreamOptions: Promise<UrlMediaStreamOptions[]>;
|
||||
onvifIntercom = new OnvifIntercom(this);
|
||||
hasSmartDetection: boolean;
|
||||
|
||||
constructor(nativeId: string, provider: RtspProvider) {
|
||||
super(nativeId, provider);
|
||||
@@ -37,6 +37,7 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
this.storage.removeItem('amcrestDoorbell');
|
||||
}
|
||||
|
||||
this.hasSmartDetection = this.storage.getItem('hasSmartDetection') === 'true';
|
||||
this.updateDevice();
|
||||
this.updateDeviceInfo();
|
||||
}
|
||||
@@ -94,12 +95,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 +147,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() {
|
||||
@@ -161,6 +161,16 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
}
|
||||
|
||||
async listenEvents() {
|
||||
let motionTimeout: NodeJS.Timeout;
|
||||
|
||||
const motionTimeoutDuration = 20000;
|
||||
const resetMotionTimeout = () => {
|
||||
clearTimeout(motionTimeout);
|
||||
motionTimeout = setTimeout(() => {
|
||||
this.motionDetected = false;
|
||||
}, motionTimeoutDuration);
|
||||
}
|
||||
|
||||
const client = new AmcrestCameraClient(this.getHttpAddress(), this.getUsername(), this.getPassword(), this.console);
|
||||
const events = await client.listenEvents();
|
||||
const doorbellType = this.storage.getItem('doorbellType');
|
||||
@@ -176,11 +186,21 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
if (idx.toString() !== channelNumber)
|
||||
return;
|
||||
}
|
||||
if (event === AmcrestEvent.MotionStart) {
|
||||
if (event === AmcrestEvent.MotionStart
|
||||
|| event === AmcrestEvent.SmartMotionHuman
|
||||
|| event === AmcrestEvent.SmartMotionVehicle
|
||||
|| event === AmcrestEvent.CrossLineDetection
|
||||
|| event === AmcrestEvent.CrossRegionDetection) {
|
||||
this.motionDetected = true;
|
||||
resetMotionTimeout();
|
||||
}
|
||||
else if (event === AmcrestEvent.MotionInfo) {
|
||||
// this seems to be a motion pulse
|
||||
if (this.motionDetected)
|
||||
resetMotionTimeout();
|
||||
}
|
||||
else if (event === AmcrestEvent.MotionStop) {
|
||||
this.motionDetected = false;
|
||||
// use resetMotionTimeout
|
||||
}
|
||||
else if (event === AmcrestEvent.AudioStart) {
|
||||
this.audioDetected = true;
|
||||
@@ -222,9 +242,43 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
}
|
||||
});
|
||||
|
||||
events.on('smart', (className: string, data: AmcrestEventData) => {
|
||||
if (!this.hasSmartDetection) {
|
||||
this.hasSmartDetection = true;
|
||||
this.storage.setItem('hasSmartDetection', 'true');
|
||||
this.updateDevice();
|
||||
}
|
||||
|
||||
const detected: ObjectsDetected = {
|
||||
timestamp: Date.now(),
|
||||
detections: [
|
||||
{
|
||||
score: 1,
|
||||
className,
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
this.onDeviceEvent(ScryptedInterface.ObjectDetector, detected);
|
||||
});
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
async getDetectionInput(detectionId: string, eventId?: any): Promise<MediaObject> {
|
||||
return;
|
||||
}
|
||||
|
||||
async getObjectTypes(): Promise<ObjectDetectionTypes> {
|
||||
return {
|
||||
classes: [
|
||||
'person',
|
||||
'face',
|
||||
'car',
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
async getOtherSettings(): Promise<Setting[]> {
|
||||
const ret = await super.getOtherSettings();
|
||||
ret.push(
|
||||
@@ -261,6 +315,16 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
|
||||
|
||||
if (doorbellType == DAHUA_DOORBELL_TYPE) {
|
||||
ret.push(
|
||||
{
|
||||
title: 'Enable Dahua Lock',
|
||||
key: 'enableDahuaLock',
|
||||
description: 'Some Dahua Doorbells have a built in lock/door access control.',
|
||||
type: 'boolean',
|
||||
value: (this.storage.getItem('enableDahuaLock') === 'true').toString(),
|
||||
}
|
||||
);
|
||||
|
||||
ret.push(
|
||||
{
|
||||
title: 'Multiple Call Buttons',
|
||||
@@ -309,11 +373,8 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async takeSmartCameraPicture(option?: PictureOptions): Promise<MediaObject> {
|
||||
return this.createMediaObject(await this.getClient().jpegSnapshot(), 'image/jpeg');
|
||||
async takeSmartCameraPicture(options?: RequestPictureOptions): Promise<MediaObject> {
|
||||
return this.createMediaObject(await this.getClient().jpegSnapshot(options?.timeout), 'image/jpeg');
|
||||
}
|
||||
|
||||
async getUrlSettings() {
|
||||
@@ -334,7 +395,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 +408,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 +425,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,9 +449,9 @@ 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';
|
||||
@@ -409,18 +466,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;
|
||||
|
||||
@@ -460,9 +517,19 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
if (isDoorbell || twoWayAudio) {
|
||||
interfaces.push(ScryptedInterface.Intercom);
|
||||
}
|
||||
|
||||
const enableDahuaLock = this.storage.getItem('enableDahuaLock') === 'true';
|
||||
if (isDoorbell && doorbellType === DAHUA_DOORBELL_TYPE && enableDahuaLock) {
|
||||
interfaces.push(ScryptedInterface.Lock);
|
||||
}
|
||||
|
||||
const continuousRecording = this.storage.getItem('continuousRecording') === 'true';
|
||||
if (continuousRecording)
|
||||
interfaces.push(ScryptedInterface.VideoRecorder);
|
||||
|
||||
if (this.hasSmartDetection)
|
||||
interfaces.push(ScryptedInterface.ObjectDetector);
|
||||
|
||||
this.provider.updateDevice(this.nativeId, this.name, interfaces, type);
|
||||
}
|
||||
|
||||
@@ -504,6 +571,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 +583,39 @@ 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';
|
||||
// args.push(
|
||||
// "-vn",
|
||||
// '-acodec', 'pcm_mulaw',
|
||||
// '-ac', '1',
|
||||
// '-ar', '8000',
|
||||
// '-sample_fmt', 's16',
|
||||
// '-f', 'mulaw',
|
||||
// 'pipe:3',
|
||||
// );
|
||||
// contentType = 'Audio/G.711A';
|
||||
}
|
||||
|
||||
this.console.log('ffmpeg intercom', args);
|
||||
|
||||
@@ -538,16 +634,19 @@ 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',
|
||||
const abortController = new AbortController();
|
||||
this.getClient().request({
|
||||
url,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'Audio/AAC',
|
||||
'Content-Length': '9999999'
|
||||
'Content-Type': contentType,
|
||||
'Content-Length': '9999999',
|
||||
},
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
data: passthrough,
|
||||
});
|
||||
signal: abortController.signal,
|
||||
responseType: 'readable',
|
||||
}, passthrough)
|
||||
.catch(() => { })
|
||||
.finally(() => this.console.log('request finished'))
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
@@ -559,7 +658,8 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
}
|
||||
finally {
|
||||
this.console.log('audio finished');
|
||||
passthrough.end();
|
||||
passthrough.destroy();
|
||||
abortController.abort();
|
||||
}
|
||||
|
||||
this.stopIntercom();
|
||||
@@ -578,6 +678,18 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
showRtspUrlOverride() {
|
||||
return false;
|
||||
}
|
||||
|
||||
async lock(): Promise<void> {
|
||||
if (!this.client.lock()) {
|
||||
this.console.error("Could not lock");
|
||||
}
|
||||
}
|
||||
|
||||
async unlock(): Promise<void> {
|
||||
if (!this.client.unlock()) {
|
||||
this.console.error("Could not unlock");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AmcrestProvider extends RtspProvider {
|
||||
@@ -598,7 +710,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,
|
||||
} = {};
|
||||
@@ -34,9 +29,14 @@ export async function getDeviceInfo(digestAuth: AxiosDigestAuth, address: string
|
||||
vals[k] = v.trim();
|
||||
}
|
||||
|
||||
return {
|
||||
const ret = {
|
||||
deviceType: vals.deviceType,
|
||||
hardwareVersion: vals.hardwareVersion,
|
||||
serialNumber: vals.serialNumber,
|
||||
}
|
||||
};
|
||||
|
||||
if (!ret.deviceType && !ret.hardwareVersion && !ret.serialNumber)
|
||||
throw new Error('not amcrest');
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
530
plugins/bticino/package-lock.json
generated
530
plugins/bticino/package-lock.json
generated
@@ -1,52 +1,48 @@
|
||||
{
|
||||
"name": "@scrypted/bticino",
|
||||
"version": "0.0.13",
|
||||
"version": "0.0.16",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/bticino",
|
||||
"version": "0.0.13",
|
||||
"version": "0.0.16",
|
||||
"dependencies": {
|
||||
"@slyoldfox/sip": "^0.0.6-1",
|
||||
"sdp": "^3.0.3",
|
||||
"stun": "^2.1.0",
|
||||
"uuid": "^8.3.2"
|
||||
"stun": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"@types/node": "^16.9.6",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"cross-env": "^7.0.3",
|
||||
"ts-node": "^10.9.1"
|
||||
}
|
||||
},
|
||||
"../../common": {
|
||||
"name": "@scrypted/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"
|
||||
"http-auth-utils": "^5.0.1",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.9.0"
|
||||
"@types/node": "^20.11.0",
|
||||
"ts-node": "^10.9.2"
|
||||
}
|
||||
},
|
||||
"../../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.3.2",
|
||||
"version": "0.3.29",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"adm-zip": "^0.4.13",
|
||||
"axios": "^0.21.4",
|
||||
"axios": "^1.6.5",
|
||||
"babel-loader": "^9.1.0",
|
||||
"babel-plugin-const-enum": "^1.1.0",
|
||||
"esbuild": "^0.15.9",
|
||||
@@ -89,18 +85,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||
"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": {
|
||||
@@ -130,9 +126,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
||||
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@tsconfig/node12": {
|
||||
@@ -148,27 +144,21 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@tsconfig/node16": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
|
||||
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
|
||||
"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": "16.18.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.16.tgz",
|
||||
"integrity": "sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "8.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
|
||||
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
|
||||
"version": "16.18.96",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.96.tgz",
|
||||
"integrity": "sha512-84iSqGXoO+Ha16j8pRZ/L90vDMKX04QTYMTfYeE1WrjWaZXuchBehGUZEpNgx7JnmlrIHdnABmpjrQjhCnNldQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.8.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
|
||||
"integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
|
||||
"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"
|
||||
@@ -178,9 +168,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
|
||||
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
|
||||
"integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
@@ -229,12 +219,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
|
||||
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"get-intrinsic": "^1.0.2"
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"set-function-length": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@@ -365,6 +361,22 @@
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/define-data-property": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
@@ -382,6 +394,25 @@
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
|
||||
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/filter-obj": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
|
||||
@@ -402,9 +433,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/generate-function": {
|
||||
"version": "2.3.1",
|
||||
@@ -415,13 +449,29 @@
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
|
||||
"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
|
||||
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.3"
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"has-proto": "^1.0.1",
|
||||
"has-symbols": "^1.0.3",
|
||||
"hasown": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.1.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@@ -432,15 +482,26 @@
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
|
||||
},
|
||||
"node_modules/has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"node_modules/has-property-descriptors": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1"
|
||||
"es-define-property": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-proto": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
|
||||
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
@@ -454,6 +515,17 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/hosted-git-info": {
|
||||
"version": "2.8.9",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
||||
@@ -468,9 +540,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ip": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
|
||||
"integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg=="
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz",
|
||||
"integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ=="
|
||||
},
|
||||
"node_modules/ip2buf": {
|
||||
"version": "2.0.0",
|
||||
@@ -486,11 +558,11 @@
|
||||
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
|
||||
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
|
||||
"version": "2.13.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
|
||||
"integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
|
||||
"dependencies": {
|
||||
"has": "^1.0.3"
|
||||
"hasown": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@@ -671,9 +743,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.12.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
|
||||
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
|
||||
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
@@ -789,11 +861,11 @@
|
||||
"integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg=="
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.11.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz",
|
||||
"integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==",
|
||||
"version": "6.12.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz",
|
||||
"integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.4"
|
||||
"side-channel": "^1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
@@ -865,11 +937,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
||||
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
|
||||
"version": "1.22.8",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
||||
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.9.0",
|
||||
"is-core-module": "^2.13.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
},
|
||||
@@ -912,6 +984,22 @@
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/set-function-length": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
||||
"dependencies": {
|
||||
"define-data-property": "^1.1.4",
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"gopd": "^1.0.1",
|
||||
"has-property-descriptors": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
@@ -934,13 +1022,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
|
||||
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.0",
|
||||
"get-intrinsic": "^1.0.2",
|
||||
"object-inspect": "^1.9.0"
|
||||
"call-bind": "^1.0.7",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"object-inspect": "^1.13.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@@ -961,9 +1053,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/spdx-exceptions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
|
||||
"integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A=="
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
|
||||
"integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="
|
||||
},
|
||||
"node_modules/spdx-expression-parse": {
|
||||
"version": "3.0.1",
|
||||
@@ -975,9 +1067,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/spdx-license-ids": {
|
||||
"version": "3.0.13",
|
||||
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz",
|
||||
"integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w=="
|
||||
"version": "3.0.17",
|
||||
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz",
|
||||
"integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg=="
|
||||
},
|
||||
"node_modules/split-on-first": {
|
||||
"version": "1.1.0",
|
||||
@@ -1054,9 +1146,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
|
||||
"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",
|
||||
@@ -1105,9 +1197,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz",
|
||||
"integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==",
|
||||
"version": "5.4.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
|
||||
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
@@ -1115,7 +1207,7 @@
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.20"
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
@@ -1126,14 +1218,6 @@
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"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",
|
||||
@@ -1193,15 +1277,15 @@
|
||||
}
|
||||
},
|
||||
"@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||
"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": {
|
||||
@@ -1219,10 +1303,10 @@
|
||||
"requires": {
|
||||
"@scrypted/sdk": "file:../sdk",
|
||||
"@scrypted/server": "file:../server",
|
||||
"@types/node": "^16.9.0",
|
||||
"http-auth-utils": "^3.0.2",
|
||||
"node-fetch-commonjs": "^3.1.1",
|
||||
"typescript": "^4.4.3"
|
||||
"@types/node": "^20.11.0",
|
||||
"http-auth-utils": "^5.0.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
},
|
||||
"@scrypted/sdk": {
|
||||
@@ -1232,7 +1316,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",
|
||||
@@ -1255,9 +1339,9 @@
|
||||
"integrity": "sha512-PJBIAKS3aMsFTHeQLfAtVpZOduAqGNZZAEH6Kb15htGUcSJWHZ9r2LAjxm3fD4yWT9plYlO0CthcEVnlrrwQLA=="
|
||||
},
|
||||
"@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
||||
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
||||
"dev": true
|
||||
},
|
||||
"@tsconfig/node12": {
|
||||
@@ -1273,33 +1357,27 @@
|
||||
"dev": true
|
||||
},
|
||||
"@tsconfig/node16": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
|
||||
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
|
||||
"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": "16.18.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.16.tgz",
|
||||
"integrity": "sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/uuid": {
|
||||
"version": "8.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
|
||||
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
|
||||
"version": "16.18.96",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.96.tgz",
|
||||
"integrity": "sha512-84iSqGXoO+Ha16j8pRZ/L90vDMKX04QTYMTfYeE1WrjWaZXuchBehGUZEpNgx7JnmlrIHdnABmpjrQjhCnNldQ==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn": {
|
||||
"version": "8.8.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
|
||||
"integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
|
||||
"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.2.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
|
||||
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
|
||||
"integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
|
||||
"dev": true
|
||||
},
|
||||
"arg": {
|
||||
@@ -1336,12 +1414,15 @@
|
||||
}
|
||||
},
|
||||
"call-bind": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
|
||||
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1",
|
||||
"get-intrinsic": "^1.0.2"
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"set-function-length": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"camelcase": {
|
||||
@@ -1427,6 +1508,16 @@
|
||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
|
||||
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="
|
||||
},
|
||||
"define-data-property": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
||||
"requires": {
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
@@ -1441,6 +1532,19 @@
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"es-define-property": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
|
||||
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
|
||||
"requires": {
|
||||
"get-intrinsic": "^1.2.4"
|
||||
}
|
||||
},
|
||||
"es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="
|
||||
},
|
||||
"filter-obj": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
|
||||
@@ -1455,9 +1559,9 @@
|
||||
}
|
||||
},
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
|
||||
},
|
||||
"generate-function": {
|
||||
"version": "2.3.1",
|
||||
@@ -1468,13 +1572,23 @@
|
||||
}
|
||||
},
|
||||
"get-intrinsic": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
|
||||
"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
|
||||
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.3"
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"has-proto": "^1.0.1",
|
||||
"has-symbols": "^1.0.3",
|
||||
"hasown": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"gopd": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
|
||||
"requires": {
|
||||
"get-intrinsic": "^1.1.3"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
@@ -1482,19 +1596,32 @@
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
|
||||
},
|
||||
"has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"has-property-descriptors": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1"
|
||||
"es-define-property": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"has-proto": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
|
||||
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q=="
|
||||
},
|
||||
"has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
|
||||
},
|
||||
"hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"requires": {
|
||||
"function-bind": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"hosted-git-info": {
|
||||
"version": "2.8.9",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
||||
@@ -1506,9 +1633,9 @@
|
||||
"integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ=="
|
||||
},
|
||||
"ip": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
|
||||
"integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg=="
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz",
|
||||
"integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ=="
|
||||
},
|
||||
"ip2buf": {
|
||||
"version": "2.0.0",
|
||||
@@ -1521,11 +1648,11 @@
|
||||
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
|
||||
},
|
||||
"is-core-module": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
|
||||
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
|
||||
"version": "2.13.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
|
||||
"integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
|
||||
"requires": {
|
||||
"has": "^1.0.3"
|
||||
"hasown": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"is-plain-obj": {
|
||||
@@ -1669,9 +1796,9 @@
|
||||
"integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="
|
||||
},
|
||||
"object-inspect": {
|
||||
"version": "1.12.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
|
||||
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g=="
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
|
||||
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ=="
|
||||
},
|
||||
"p-limit": {
|
||||
"version": "1.3.0",
|
||||
@@ -1760,11 +1887,11 @@
|
||||
"integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg=="
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.11.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz",
|
||||
"integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==",
|
||||
"version": "6.12.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz",
|
||||
"integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==",
|
||||
"requires": {
|
||||
"side-channel": "^1.0.4"
|
||||
"side-channel": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"query-string": {
|
||||
@@ -1812,11 +1939,11 @@
|
||||
}
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.22.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
||||
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
|
||||
"version": "1.22.8",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
||||
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
|
||||
"requires": {
|
||||
"is-core-module": "^2.9.0",
|
||||
"is-core-module": "^2.13.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
}
|
||||
@@ -1836,6 +1963,19 @@
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="
|
||||
},
|
||||
"set-function-length": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
||||
"requires": {
|
||||
"define-data-property": "^1.1.4",
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"gopd": "^1.0.1",
|
||||
"has-property-descriptors": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
@@ -1852,13 +1992,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"side-channel": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
|
||||
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
|
||||
"requires": {
|
||||
"call-bind": "^1.0.0",
|
||||
"get-intrinsic": "^1.0.2",
|
||||
"object-inspect": "^1.9.0"
|
||||
"call-bind": "^1.0.7",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"object-inspect": "^1.13.1"
|
||||
}
|
||||
},
|
||||
"signal-exit": {
|
||||
@@ -1876,9 +2017,9 @@
|
||||
}
|
||||
},
|
||||
"spdx-exceptions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
|
||||
"integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A=="
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
|
||||
"integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="
|
||||
},
|
||||
"spdx-expression-parse": {
|
||||
"version": "3.0.1",
|
||||
@@ -1890,9 +2031,9 @@
|
||||
}
|
||||
},
|
||||
"spdx-license-ids": {
|
||||
"version": "3.0.13",
|
||||
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz",
|
||||
"integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w=="
|
||||
"version": "3.0.17",
|
||||
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz",
|
||||
"integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg=="
|
||||
},
|
||||
"split-on-first": {
|
||||
"version": "1.1.0",
|
||||
@@ -1942,9 +2083,9 @@
|
||||
"integrity": "sha512-MTBWv3jhVjTU7XR3IQHllbiJs8sc75a80OEhB6or/q7pLTWgQ0bMGQXXYQSrSuXe6WiKWDZ5txXY5P59a/coVA=="
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
|
||||
"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": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
@@ -1968,9 +2109,9 @@
|
||||
"integrity": "sha512-8yyRd1ZdNp+AQLGqi3lTaA2k81JjlIZOyFQEsi7GQWBgirnQOxjqVtDEbYHM2Z4yFdJ5AQw0fxBLLnDCl6RXoQ=="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz",
|
||||
"integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==",
|
||||
"version": "5.4.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
|
||||
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
@@ -1979,11 +2120,6 @@
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
|
||||
},
|
||||
"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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/bticino",
|
||||
"version": "0.0.13",
|
||||
"version": "0.0.16",
|
||||
"scripts": {
|
||||
"scrypted-setup-project": "scrypted-setup-project",
|
||||
"prescrypted-setup-project": "scrypted-package-json",
|
||||
@@ -34,14 +34,12 @@
|
||||
"dependencies": {
|
||||
"@slyoldfox/sip": "^0.0.6-1",
|
||||
"sdp": "^3.0.3",
|
||||
"stun": "^2.1.0",
|
||||
"uuid": "^8.3.2"
|
||||
"stun": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"@types/node": "^16.9.6",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"cross-env": "^7.0.3",
|
||||
"ts-node": "^10.9.1"
|
||||
}
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
import { closeQuiet, createBindUdp, createBindZero, listenZeroSingleClient } from '@scrypted/common/src/listen-cluster';
|
||||
import { createBindUdp, listenZeroSingleClient } from '@scrypted/common/src/listen-cluster';
|
||||
import { sleep } from '@scrypted/common/src/sleep';
|
||||
import { RtspServer } from '@scrypted/common/src/rtsp-server';
|
||||
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 sdk, { BinarySensor, Camera, DeviceProvider, FFmpegInput, HttpRequest, HttpRequestHandler, HttpResponse, Intercom, MediaObject, MediaStreamUrl, MotionSensor, PictureOptions, Reboot, ResponseMediaStreamOptions, ScryptedDeviceBase, ScryptedInterface, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera, VideoClip, VideoClipOptions, VideoClips } from '@scrypted/sdk';
|
||||
import { SipCallSession } from '../../sip/src/sip-call-session';
|
||||
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';
|
||||
import child_process, { ChildProcess } from 'child_process';
|
||||
import dgram from 'dgram';
|
||||
import { BticinoStorageSettings } from './storage-settings';
|
||||
import { BticinoSipPlugin } from './main';
|
||||
import { BticinoSipLock } from './bticino-lock';
|
||||
import { ffmpegLogInitialOutput, safeKillFFmpeg, safePrintFFmpegArguments } from '@scrypted/common/src/media-helpers';
|
||||
import { safePrintFFmpegArguments } from '@scrypted/common/src/media-helpers';
|
||||
import { PersistentSipManager } from './persistent-sip-manager';
|
||||
import { InviteHandler } from './bticino-inviteHandler';
|
||||
import { SipOptions, SipRequest } from '../../sip/src/sip-manager';
|
||||
import { startRtpForwarderProcess } from '../../webrtc/src/rtp-forwarders';
|
||||
import fs from "fs"
|
||||
import url from "url"
|
||||
import path from 'path';
|
||||
import { default as stream } from 'node:stream'
|
||||
import type { ReadableStream } from 'node:stream/web'
|
||||
import { finished } from "stream/promises";
|
||||
|
||||
import { get } from 'http'
|
||||
import { ControllerApi } from './c300x-controller-api';
|
||||
@@ -25,13 +31,13 @@ import { BticinoMuteSwitch } from './bticino-mute-switch';
|
||||
|
||||
const STREAM_TIMEOUT = 65000;
|
||||
const { mediaManager } = sdk;
|
||||
const BTICINO_CLIPS = path.join(process.env.SCRYPTED_PLUGIN_VOLUME, 'bticino-clips');
|
||||
|
||||
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 forwarder
|
||||
private refreshTimeout: NodeJS.Timeout
|
||||
public requestHandlers: CompositeSipMessageHandler = new CompositeSipMessageHandler()
|
||||
public incomingCallRequest : SipRequest
|
||||
@@ -147,11 +153,87 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements MotionSensor
|
||||
});
|
||||
}
|
||||
|
||||
getVideoClip(videoId: string): Promise<MediaObject> {
|
||||
let c300x = SipHelper.getIntercomIp(this)
|
||||
const url = `http://${c300x}:8080/voicemail?msg=${videoId}/aswm.avi&raw=true`;
|
||||
return mediaManager.createMediaObjectFromUrl(url);
|
||||
async getVideoClip(videoId: string): Promise<MediaObject> {
|
||||
const outputfile = await this.fetchAndConvertVoicemailMessage(videoId);
|
||||
|
||||
const fileURLToPath: string = url.pathToFileURL(outputfile).toString()
|
||||
this.console.log(`Creating mediaObject for url: ${fileURLToPath}`)
|
||||
return await mediaManager.createMediaObjectFromUrl(fileURLToPath);
|
||||
}
|
||||
|
||||
private async fetchAndConvertVoicemailMessage(videoId: string) {
|
||||
let c300x = SipHelper.getIntercomIp(this)
|
||||
|
||||
const response = await fetch(`http://${c300x}:8080/voicemail?msg=${videoId}/aswm.avi&raw=true`);
|
||||
|
||||
const contentLength: number = Number(response.headers.get("Content-Length"));
|
||||
const lastModified: Date = new Date(response.headers.get("Last-Modified-Time"));
|
||||
|
||||
const avifile = `${BTICINO_CLIPS}/${videoId}.avi`;
|
||||
const outputfile = `${BTICINO_CLIPS}/${videoId}.mp4`;
|
||||
|
||||
if (!fs.existsSync(BTICINO_CLIPS)) {
|
||||
this.console.log(`Creating clips dir at: ${BTICINO_CLIPS}`)
|
||||
fs.mkdirSync(BTICINO_CLIPS);
|
||||
}
|
||||
|
||||
if (fs.existsSync(avifile)) {
|
||||
const stat = fs.statSync(avifile);
|
||||
if (stat.size != contentLength || stat.mtime.getTime() != lastModified.getTime()) {
|
||||
this.console.log(`Size ${stat.size} != ${contentLength} or time ${stat.mtime.getTime} != ${lastModified.getTime}`)
|
||||
try {
|
||||
fs.rmSync(avifile);
|
||||
} catch (e) { }
|
||||
try {
|
||||
fs.rmSync(outputfile);
|
||||
} catch (e) { }
|
||||
} else {
|
||||
this.console.log(`Keeping the cached video at ${avifile}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (!fs.existsSync(avifile)) {
|
||||
this.console.log("Starting download.")
|
||||
await finished(stream.Readable.from(response.body as ReadableStream<Uint8Array>).pipe(fs.createWriteStream(avifile)));
|
||||
this.console.log("Download finished.")
|
||||
try {
|
||||
this.console.log(`Setting mtime to ${lastModified}`)
|
||||
fs.utimesSync(avifile, lastModified, lastModified);
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
const ffmpegPath = await mediaManager.getFFmpegPath();
|
||||
const ffmpegArgs = [
|
||||
'-hide_banner',
|
||||
'-nostats',
|
||||
'-y',
|
||||
'-i', avifile,
|
||||
outputfile
|
||||
];
|
||||
|
||||
safePrintFFmpegArguments(console, ffmpegArgs);
|
||||
const cp = child_process.spawn(ffmpegPath, ffmpegArgs, {
|
||||
stdio: ['pipe', 'pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
const p = new Promise((resolveFunc) => {
|
||||
cp.stdout.on("data", (x) => {
|
||||
this.console.log(x.toString());
|
||||
});
|
||||
cp.stderr.on("data", (x) => {
|
||||
this.console.error(x.toString());
|
||||
});
|
||||
cp.on("exit", (code) => {
|
||||
resolveFunc(code);
|
||||
});
|
||||
});
|
||||
|
||||
let returnCode = await p;
|
||||
|
||||
this.console.log(`Converted file returned code: ${returnCode}`);
|
||||
return outputfile;
|
||||
}
|
||||
|
||||
getVideoClipThumbnail(thumbnailId: string): Promise<MediaObject> {
|
||||
let c300x = SipHelper.getIntercomIp(this)
|
||||
const url = `http://${c300x}:8080/voicemail?msg=${thumbnailId}/aswm.jpg&raw=true`;
|
||||
@@ -193,21 +275,27 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements MotionSensor
|
||||
}
|
||||
|
||||
async takePicture(option?: PictureOptions): Promise<MediaObject> {
|
||||
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}`)
|
||||
|
||||
let rebroadcastEnabled = this.interfaces?.includes( "mixin:@scrypted/prebuffer-mixin")
|
||||
if( rebroadcastEnabled ) {
|
||||
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')
|
||||
} else {
|
||||
this.console.log(`Not refreshing camera picture: ${this.lastImageRefresh} + cache time: ${thumbnailCacheTime} < ${now}`)
|
||||
throw new Error("To enable snapshots, enable rebroadcast plugin or set a Snapshot URL in the Snapshot plugin to an external image.");
|
||||
}
|
||||
return mediaManager.createMediaObject(this.cachedImage, 'image/jpeg')
|
||||
}
|
||||
|
||||
async getPictureOptions(): Promise<PictureOptions[]> {
|
||||
@@ -234,52 +322,31 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements MotionSensor
|
||||
this.session = await this.callIntercom( cleanup )
|
||||
}
|
||||
|
||||
|
||||
this.stopIntercom();
|
||||
|
||||
const ffmpegInput: FFmpegInput = JSON.parse((await mediaManager.convertMediaObjectToBuffer(media, ScryptedMimeTypes.FFmpegInput)).toString());
|
||||
|
||||
const audioOutForwarder = await createBindZero()
|
||||
this.audioOutForwarder = audioOutForwarder.server
|
||||
const ffmpegInput = await sdk.mediaManager.convertMediaObjectToJSON<FFmpegInput>(media, ScryptedMimeTypes.FFmpegInput);
|
||||
let address = (await this.remoteRtpDescription).address
|
||||
audioOutForwarder.server.on('message', message => {
|
||||
if( this.session )
|
||||
this.session.audioSplitter.send(message, 40004, address)
|
||||
return null
|
||||
});
|
||||
|
||||
const args = ffmpegInput.inputArguments.slice();
|
||||
args.push(
|
||||
'-vn', '-dn', '-sn',
|
||||
'-acodec', 'speex',
|
||||
'-flags', '+global_header',
|
||||
'-ac', '1',
|
||||
'-ar', '8k',
|
||||
'-f', 'rtp',
|
||||
//'-srtp_out_suite', 'AES_CM_128_HMAC_SHA1_80',
|
||||
//'-srtp_out_params', encodeSrtpOptions(this.decodedSrtpOptions),
|
||||
`rtp://127.0.0.1:${audioOutForwarder.port}?pkt_size=188`,
|
||||
);
|
||||
|
||||
this.console.log("===========================================")
|
||||
safePrintFFmpegArguments( this.console, args )
|
||||
this.console.log("===========================================")
|
||||
|
||||
const cp = child_process.spawn(await mediaManager.getFFmpegPath(), args);
|
||||
ffmpegLogInitialOutput(this.console, cp)
|
||||
this.audioOutProcess = cp;
|
||||
cp.on('exit', () => this.console.log('two way audio ended'));
|
||||
this.session.onCallEnded.subscribe(() => {
|
||||
closeQuiet(audioOutForwarder.server);
|
||||
safeKillFFmpeg(cp)
|
||||
this.forwarder = await startRtpForwarderProcess(this.console, ffmpegInput, {
|
||||
audio: {
|
||||
codecCopy: 'speex',
|
||||
encoderArguments: [
|
||||
'-vn', '-sn', '-dn',
|
||||
'-acodec', 'speex',
|
||||
'-flags', '+global_header',
|
||||
'-ac', '1',
|
||||
'-ar', '8k',
|
||||
'-f', 'rtp',
|
||||
],
|
||||
onRtp: rtp => {
|
||||
this.session?.audioSplitter?.send(rtp, 40004, address)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async stopIntercom(): Promise<void> {
|
||||
closeQuiet(this.audioOutForwarder)
|
||||
this.audioOutProcess?.kill('SIGKILL')
|
||||
this.audioOutProcess = undefined
|
||||
this.audioOutForwarder = undefined
|
||||
this.forwarder?.kill()
|
||||
this.forwarder = undefined
|
||||
}
|
||||
|
||||
resetStreamTimeout() {
|
||||
@@ -489,12 +556,24 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements MotionSensor
|
||||
// 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}`,
|
||||
]
|
||||
let audioSection = [
|
||||
// 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}`,
|
||||
]
|
||||
if( !this.incomingCallRequest ) {
|
||||
let DEVADDR = this.storage.getItem('DEVADDR');
|
||||
if( DEVADDR ) {
|
||||
audioSection.unshift('a=DEVADDR:' + DEVADDR)
|
||||
} else {
|
||||
if( sipOptions.to.toLocaleLowerCase().indexOf('c300x') >= 0 || sipOptions.to.toLocaleLowerCase().indexOf('c100x') >= 0 ) {
|
||||
// Needed for bt_answering_machine (bticino specific), to check for c100X
|
||||
audioSection.unshift('a=DEVADDR:20')
|
||||
}
|
||||
}
|
||||
}
|
||||
return audioSection
|
||||
}, ( 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
|
||||
|
||||
@@ -33,8 +33,10 @@ export class VoicemailHandler extends SipRequestHandler {
|
||||
handle(request: SipRequest) {
|
||||
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*');
|
||||
let matches : Array<RegExpMatchArray> = [...message.matchAll(/\*#8\*\*40\*([01])\*([01])\*/gm)]
|
||||
if( matches && matches.length > 0 && matches[0].length > 0 ) {
|
||||
this.sipCamera.console.debug( "Answering machine state: " + matches[0][1] + " / Welcome message state: " + matches[0][2] );
|
||||
this.aswmIsEnabled = matches[0][1] == '1';
|
||||
if( this.isEnabled() ) {
|
||||
this.sipCamera.console.debug("Handling incoming answering machine reply")
|
||||
const messages : string[] = message.split(';')
|
||||
@@ -60,6 +62,8 @@ export class VoicemailHandler extends SipRequestHandler {
|
||||
this.sipCamera.console.debug("No new messages since: " + lastVoicemailMessageTimestamp + " lastMessage: " + lastMessageTimestamp)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.sipCamera.console.debug("Not handling message: " + message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
4
plugins/chromecast/package-lock.json
generated
4
plugins/chromecast/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/chromecast",
|
||||
"version": "0.1.56",
|
||||
"version": "0.1.58",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/chromecast",
|
||||
"version": "0.1.56",
|
||||
"version": "0.1.58",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/chromecast",
|
||||
"version": "0.1.56",
|
||||
"version": "0.1.58",
|
||||
"description": "Send video, audio, and text to speech notifications to Chromecast and Google Home devices",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -183,7 +183,7 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng
|
||||
media = await mediaManager.createMediaObjectFromUrl(media);
|
||||
}
|
||||
}
|
||||
else if (options?.mimeType?.startsWith('image/')) {
|
||||
else if (options?.mimeType?.startsWith('image/') || options?.mimeType?.startsWith('audio/')) {
|
||||
url = await mediaManager.convertMediaObjectToInsecureLocalUrl(media, options?.mimeType);
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng
|
||||
let cameraStreamAuthToken: string;
|
||||
|
||||
try {
|
||||
cameraStreamAuthToken= await mediaManager.convertMediaObjectToUrl(mo, ScryptedMimeTypes.LocalUrl);
|
||||
cameraStreamAuthToken = await mediaManager.convertMediaObjectToUrl(mo, ScryptedMimeTypes.LocalUrl);
|
||||
}
|
||||
catch (e) {
|
||||
this.log.a('Streaming failed. Install and set up Scrypted Cloud to cast this media type.');
|
||||
@@ -469,6 +469,12 @@ class CastDeviceProvider extends ScryptedDeviceBase implements DeviceProvider {
|
||||
constructor() {
|
||||
super(null);
|
||||
|
||||
endpointManager.setAccessControlAllowOrigin({
|
||||
origins: [
|
||||
// chromecast receiver
|
||||
'https://koush.github.io',
|
||||
],
|
||||
});
|
||||
|
||||
this.browser.on('response', response => {
|
||||
for (const additional of response.additionals) {
|
||||
@@ -562,7 +568,7 @@ class CastDeviceProvider extends ScryptedDeviceBase implements DeviceProvider {
|
||||
}
|
||||
|
||||
async releaseDevice(id: string, nativeId: string): Promise<void> {
|
||||
|
||||
|
||||
}
|
||||
|
||||
async discoverDevices(duration: number) {
|
||||
|
||||
2
plugins/cloud/.vscode/launch.json
vendored
2
plugins/cloud/.vscode/launch.json
vendored
@@ -16,7 +16,7 @@
|
||||
"sourceMaps": true,
|
||||
"localRoot": "${workspaceFolder}/out",
|
||||
"remoteRoot": "/plugin/",
|
||||
"type": "pwa-node"
|
||||
"type": "node"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -6,7 +6,8 @@
|
||||
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.
|
||||
|
||||
The network's router must configure an external port, the `From Port`, to the send traffic to the `Forward Port` on this server. These ports have random defaults that can be seen in the plugin Settings, and can be changed if preferred. Ports 10443 and 10444 are already being used by Scrypted itself, and should not be used. Choose another port, like 11443.
|
||||
|
||||
### What You'll Need
|
||||
- Access to your router's settings (usually through a web browser).
|
||||
@@ -18,24 +19,27 @@ See below for additional recommendations.
|
||||
- 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).
|
||||
- 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."
|
||||
- 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.
|
||||
- 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.
|
||||
- Go back to Scrypted and navigate to the "General" tab in the Cloud plugin.
|
||||
- Select "Router Forward" from the "Port Forwarding Mode" dropdown menu.
|
||||
|
||||
7. **Save Your Settings**
|
||||
- Don't forget to save your changes in both your router and in Scrypted.
|
||||
6. **Save Your Settings**
|
||||
- Don't forget to save your changes in both your router and in Scrypted.
|
||||
|
||||
7. **Reload Plugin**
|
||||
- After all configuration is complete, Reload Cloud Plugin to ensure the new settings are applied.
|
||||
|
||||
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 everything is set up correctly.
|
||||
|
||||
### 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.
|
||||
@@ -46,7 +50,6 @@ Custom Domains can be used with the Cloud Plugin.
|
||||
|
||||
Set up a reverse proxy to the https Forward Port shown in settings.
|
||||
|
||||
|
||||
## Cloudflare Tunnels
|
||||
|
||||
Scrypted Cloud automatically creates a login free tunnel for remote access.
|
||||
|
||||
2
plugins/cloud/external/node-nat-upnp
vendored
2
plugins/cloud/external/node-nat-upnp
vendored
Submodule plugins/cloud/external/node-nat-upnp updated: 254dd7993f...9488d7d68d
3227
plugins/cloud/package-lock.json
generated
3227
plugins/cloud/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -38,10 +38,9 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@eneris/push-receiver": "^3.1.4",
|
||||
"@eneris/push-receiver": "^3.1.5",
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"axios": "^1.4.0",
|
||||
"bpmux": "^8.2.1",
|
||||
"cloudflared": "^0.4.0",
|
||||
"exponential-backoff": "^3.1.1",
|
||||
@@ -49,10 +48,10 @@
|
||||
"nat-upnp": "file:./external/node-nat-upnp"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/http-proxy": "^1.17.11",
|
||||
"@types/ip": "^1.1.0",
|
||||
"@types/nat-upnp": "^1.1.2",
|
||||
"@types/node": "^20.4.5"
|
||||
"@types/http-proxy": "^1.17.14",
|
||||
"@types/ip": "^1.1.3",
|
||||
"@types/nat-upnp": "^1.1.5",
|
||||
"@types/node": "^20.11.19"
|
||||
},
|
||||
"version": "0.2.4"
|
||||
"version": "0.2.13"
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { Deferred } from "@scrypted/common/src/deferred";
|
||||
import sdk, { BufferConverter, DeviceProvider, HttpRequest, HttpRequestHandler, HttpResponse, OauthClient, PushHandler, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, Settings } from "@scrypted/sdk";
|
||||
import { StorageSettings } from "@scrypted/sdk/storage-settings";
|
||||
import axios from 'axios';
|
||||
import bpmux from 'bpmux';
|
||||
import * as cloudflared from 'cloudflared';
|
||||
import crypto from 'crypto';
|
||||
import { once } from 'events';
|
||||
import { backOff } from "exponential-backoff";
|
||||
import fs, { mkdirSync, renameSync, rmSync } from 'fs';
|
||||
import http from 'http';
|
||||
import HttpProxy from 'http-proxy';
|
||||
import https from 'https';
|
||||
@@ -11,24 +14,20 @@ import upnp from 'nat-upnp';
|
||||
import net from 'net';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import { Duplex, Readable } from 'stream';
|
||||
import { Duplex } from 'stream';
|
||||
import tls from 'tls';
|
||||
import { createSelfSignedCertificate } from '../../../server/src/cert';
|
||||
import { PushManager } from './push';
|
||||
import { readLine } from '../../../common/src/read-stream';
|
||||
import { createSelfSignedCertificate } from '../../../server/src/cert';
|
||||
import { httpFetch } from '../../../server/src/fetch/http-fetch';
|
||||
import { PushManager } from './push';
|
||||
import { qsparse, qsstringify } from "./qs";
|
||||
import * as cloudflared from 'cloudflared';
|
||||
import fs, { mkdirSync, renameSync, rmSync } from 'fs';
|
||||
import { backOff } from "exponential-backoff";
|
||||
import ip from 'ip';
|
||||
import { Deferred } from "@scrypted/common/src/deferred";
|
||||
|
||||
// import { registerDuckDns } from "./greenlock";
|
||||
|
||||
const { deviceManager, endpointManager, systemManager } = sdk;
|
||||
|
||||
export const DEFAULT_SENDER_ID = '827888101440';
|
||||
const SCRYPTED_SERVER = 'home.scrypted.app';
|
||||
const SCRYPTED_SERVER = localStorage.getItem('scrypted-server') || 'home.scrypted.app';
|
||||
|
||||
const SCRYPTED_CLOUD_MESSAGE_PATH = '/_punch/cloudmessage';
|
||||
|
||||
@@ -69,11 +68,16 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
},
|
||||
registrationSecret: {
|
||||
hide: true,
|
||||
persistedDefaultValue: crypto.randomBytes(8).toString('base64'),
|
||||
},
|
||||
cloudMessageToken: {
|
||||
hide: true,
|
||||
persistedDefaultValue: crypto.randomBytes(8).toString('hex'),
|
||||
},
|
||||
serverId: {
|
||||
hide: true,
|
||||
persistedDefaultValue: crypto.randomBytes(8).toString('hex'),
|
||||
},
|
||||
forwardingMode: {
|
||||
title: "Port Forwarding Mode",
|
||||
description: "The port forwarding mode used to expose the HTTPS port. If port forwarding is disabled or unavailable, Scrypted Cloud will fall back to push to initiate connections with this Scrypted server. Port Forwarding and UPNP are optional but will significantly speed up cloud connections.",
|
||||
@@ -152,7 +156,7 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
json: true,
|
||||
},
|
||||
cloudflareEnabled: {
|
||||
group: 'Advanced',
|
||||
group: 'Cloudflare',
|
||||
title: 'Cloudflare',
|
||||
type: 'boolean',
|
||||
description: 'Optional: Create a Cloudflare Tunnel to this server at a random domain name. Providing a Cloudflare token will allow usage of a custom domain name.',
|
||||
@@ -160,7 +164,7 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
onPut: () => deviceManager.requestRestart(),
|
||||
},
|
||||
cloudflaredTunnelToken: {
|
||||
group: 'Advanced',
|
||||
group: 'Cloudflare',
|
||||
title: 'Cloudflare Tunnel Token',
|
||||
description: 'Optional: Enter the Cloudflare token from the Cloudflare Dashbaord to track and manage the tunnel remotely.',
|
||||
onPut: () => {
|
||||
@@ -168,14 +172,27 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
},
|
||||
},
|
||||
cloudflaredTunnelUrl: {
|
||||
group: 'Advanced',
|
||||
group: 'Cloudflare',
|
||||
title: 'Cloudflare Tunnel URL',
|
||||
description: 'Cloudflare Tunnel URL is a randomized cloud connection, unless a Cloudflare Tunnel Token is provided.',
|
||||
readonly: true,
|
||||
mapGet: () => this.cloudflareTunnel || 'Unavailable',
|
||||
},
|
||||
serverName: {
|
||||
group: 'Connection',
|
||||
title: 'Server Name',
|
||||
description: 'The name of this server. This is used to identify this server in the Scrypted Cloud.',
|
||||
persistedDefaultValue: os.hostname()?.split('.')[0] || 'Scrypted Server',
|
||||
},
|
||||
connectHomeScryptedApp: {
|
||||
group: 'Connection',
|
||||
title: `Connect to ${SCRYPTED_SERVER}`,
|
||||
description: `Connect this server to ${SCRYPTED_SERVER}. This is required to use the Scrypted Cloud.`,
|
||||
type: 'boolean',
|
||||
persistedDefaultValue: true,
|
||||
},
|
||||
register: {
|
||||
group: 'Advanced',
|
||||
group: 'Connection',
|
||||
title: 'Register',
|
||||
type: 'button',
|
||||
onPut: () => {
|
||||
@@ -184,7 +201,7 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
description: 'Register server with Scrypted Cloud.',
|
||||
},
|
||||
testPortForward: {
|
||||
group: 'Advanced',
|
||||
group: 'Connection',
|
||||
title: 'Test Port Forward',
|
||||
type: 'button',
|
||||
onPut: () => this.testPortForward(),
|
||||
@@ -243,7 +260,7 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
|
||||
this.storageSettings.settings.securePort.onGet = async () => {
|
||||
return {
|
||||
group: this.storageSettings.values.forwardingMode === 'Disabled' ? 'Advanced' : undefined,
|
||||
group: this.storageSettings.values.forwardingMode === 'Disabled' ? 'Cloudflare' : undefined,
|
||||
title: this.storageSettings.values.forwardingMode === 'Disabled' ? 'Cloudflare Port' : 'Forward Port',
|
||||
}
|
||||
};
|
||||
@@ -288,25 +305,35 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
if (!this.storageSettings.values.certificate)
|
||||
this.storageSettings.values.certificate = createSelfSignedCertificate();
|
||||
|
||||
this.setupProxyServer();
|
||||
const proxy = this.setupProxyServer();
|
||||
this.setupCloudPush();
|
||||
|
||||
this.manager.on('registrationId', async (registrationId) => {
|
||||
// currently the fcm registration id never changes, so, there's no need.
|
||||
// if ever adding clockwork push, uncomment this.
|
||||
this.sendRegistrationId(registrationId);
|
||||
});
|
||||
|
||||
this.manager.registrationId.then(async registrationId => {
|
||||
if (this.storageSettings.values.lastPersistedRegistrationId !== registrationId || !this.storageSettings.values.registrationSecret)
|
||||
this.sendRegistrationId(registrationId);
|
||||
})
|
||||
|
||||
this.updateCors();
|
||||
|
||||
const observeRegistrations = () => {
|
||||
this.manager.on('registrationId', async (registrationId) => {
|
||||
// currently the fcm registration id never changes, so, there's no need.
|
||||
// if ever adding clockwork push, uncomment this.
|
||||
this.sendRegistrationId(registrationId);
|
||||
});
|
||||
|
||||
this.upnpInterval = setInterval(() => this.refreshPortForward(), 30 * 60 * 1000);
|
||||
this.refreshPortForward();
|
||||
}
|
||||
|
||||
if (!this.storageSettings.values.token_info && process.env.SCRYPTED_CLOUD_TOKEN) {
|
||||
this.storageSettings.values.token_info = process.env.SCRYPTED_CLOUD_TOKEN;
|
||||
this.manager.registrationId.then(r => this.sendRegistrationId(r));
|
||||
this.manager.registrationId.then(r => {
|
||||
this.sendRegistrationId(r, true);
|
||||
proxy.then(observeRegistrations);
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.manager.registrationId.then(async registrationId => {
|
||||
if (this.storageSettings.values.lastPersistedRegistrationId !== registrationId)
|
||||
this.sendRegistrationId(registrationId);
|
||||
});
|
||||
|
||||
proxy.then(observeRegistrations);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,7 +361,9 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
const url = new URL('https://www.duckdns.org/update');
|
||||
url.searchParams.set('domains', this.storageSettings.values.duckDnsHostname);
|
||||
url.searchParams.set('token', this.storageSettings.values.duckDnsToken);
|
||||
await axios(url.toString());
|
||||
await httpFetch({
|
||||
url: url.toString(),
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
this.console.error('Duck DNS Erorr', e);
|
||||
@@ -361,11 +390,16 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
|
||||
ip = this.storageSettings.values.duckDnsHostname;
|
||||
}
|
||||
else if (this.cloudflareTunnelHost) {
|
||||
ip = this.cloudflareTunnelHost;
|
||||
}
|
||||
else {
|
||||
ip = (await axios(`https://${SCRYPTED_SERVER}/_punch/ip`)).data.ip;
|
||||
if (!this.cloudflareTunnelHost) {
|
||||
ip = (await httpFetch({
|
||||
url: `https://${SCRYPTED_SERVER}/_punch/ip`,
|
||||
responseType: 'json',
|
||||
})).body.ip;
|
||||
}
|
||||
|
||||
if (this.cloudflareTunnelHost)
|
||||
ip = this.cloudflareTunnelHost
|
||||
}
|
||||
|
||||
if (this.storageSettings.values.forwardingMode === 'Custom Domain' || this.cloudflareTunnelHost)
|
||||
@@ -379,6 +413,8 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
|
||||
const registrationId = await this.manager.registrationId;
|
||||
const data = await this.sendRegistrationId(registrationId);
|
||||
if (data?.error)
|
||||
return;
|
||||
if (ip !== 'localhost' && ip !== data.ip_address && ip !== this.cloudflareTunnelHost) {
|
||||
this.log.a(`Scrypted Cloud could not verify the IP Address of your custom domain ${this.storageSettings.values.hostname}.`);
|
||||
}
|
||||
@@ -400,11 +436,14 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
if (!hostname)
|
||||
hostname = 'localhost';
|
||||
url.searchParams.set('url', `https://${hostname}:${upnp_port}${pluginPath}/testPortForward`);
|
||||
const response = await axios(url.toString());
|
||||
this.console.log('test data:', response.data);
|
||||
if (response.data.error)
|
||||
throw new Error(response.data.error);
|
||||
if (response.data.data !== this.randomBytes)
|
||||
const response = await httpFetch({
|
||||
url: url.toString(),
|
||||
responseType: 'json',
|
||||
});
|
||||
this.console.log('test data:', response.body);
|
||||
if (response.body.error)
|
||||
throw new Error(response.body.error);
|
||||
if (response.body.data !== this.randomBytes)
|
||||
throw new Error('Server received data that did not match this server.');
|
||||
this.log.a("Port Forward Test Succeeded.");
|
||||
}
|
||||
@@ -418,7 +457,7 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
let { upnpPort } = this.storageSettings.values;
|
||||
|
||||
if (!upnpPort)
|
||||
upnpPort = Math.round(Math.random() * 30000 + 20000);
|
||||
upnpPort = Math.round(Math.random() * 20000 + 40000);
|
||||
|
||||
if (this.storageSettings.values.forwardingMode === 'Disabled') {
|
||||
this.updatePortForward(upnpPort);
|
||||
@@ -494,17 +533,18 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
scope: local.pathname,
|
||||
ttl,
|
||||
})
|
||||
const scope = await axios(`https://${this.getHostname()}/_punch/scope?${q}`, {
|
||||
const scope = await httpFetch({
|
||||
url: `https://${this.getHostname()}/_punch/scope?${q}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token_info}`
|
||||
},
|
||||
responseType: 'json',
|
||||
})
|
||||
|
||||
const { userToken, userTokenSignature } = scope.data;
|
||||
const { userToken, userTokenSignature } = scope.body;
|
||||
const tokens = qsstringify({
|
||||
user_token: userToken,
|
||||
user_token_signature: userTokenSignature
|
||||
})
|
||||
});
|
||||
|
||||
const url = `${baseUrl}${local.pathname}?${tokens}`;
|
||||
this.whitelisted.set(local.pathname, url);
|
||||
@@ -517,8 +557,6 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
origins: [
|
||||
`http://${SCRYPTED_SERVER}`,
|
||||
`https://${SCRYPTED_SERVER}`,
|
||||
// chromecast receiver. move this into google home and chromecast plugins?
|
||||
'https://koush.github.io',
|
||||
...this.storageSettings.values.additionalCorsOrigins,
|
||||
],
|
||||
});
|
||||
@@ -542,50 +580,89 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
}
|
||||
|
||||
getAuthority() {
|
||||
const upnp_port = this.storageSettings.values.forwardingMode === 'Custom Domain' ? 443 : this.storageSettings.values.upnpPort;
|
||||
const hostname = this.storageSettings.values.forwardingMode === 'Custom Domain'
|
||||
const { forwardingMode } = this.storageSettings.values;
|
||||
if (forwardingMode === 'Disabled')
|
||||
return {};
|
||||
|
||||
const upnp_port = forwardingMode === 'Custom Domain' ? 443 : this.storageSettings.values.upnpPort;
|
||||
const hostname = forwardingMode === 'Custom Domain'
|
||||
? this.storageSettings.values.hostname
|
||||
: this.storageSettings.values.duckDnsToken && this.storageSettings.values.duckDnsHostname;
|
||||
|
||||
if (upnp_port === 443 && !hostname) {
|
||||
const error = this.storageSettings.values.forwardingMode === 'Custom Domain'
|
||||
const error = forwardingMode === 'Custom Domain'
|
||||
? 'Hostname is required for port Custom Domain setup.'
|
||||
: 'Port 443 requires Custom Domain configuration.';
|
||||
this.log.a(error);
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
if (!hostname) {
|
||||
return {
|
||||
upnp_port,
|
||||
port: upnp_port,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
upnp_port,
|
||||
port: upnp_port,
|
||||
hostname,
|
||||
}
|
||||
}
|
||||
|
||||
async sendRegistrationId(registration_id: string) {
|
||||
const { upnp_port, hostname } = this.getAuthority();
|
||||
const registration_secret = this.storageSettings.values.registrationSecret || crypto.randomBytes(8).toString('base64');
|
||||
async sendRegistrationId(registration_id: string, force?: boolean) {
|
||||
const authority = this.getAuthority();
|
||||
|
||||
const q = qsstringify({
|
||||
upnp_port,
|
||||
...authority,
|
||||
registration_id,
|
||||
server_id: this.storageSettings.values.serverId,
|
||||
server_name: this.storageSettings.values.serverName,
|
||||
sender_id: DEFAULT_SENDER_ID,
|
||||
registration_secret,
|
||||
hostname,
|
||||
registration_secret: this.storageSettings.values.registrationSecret,
|
||||
force: force ? 'true' : '',
|
||||
});
|
||||
|
||||
if (!this.storageSettings.values.connectHomeScryptedApp) {
|
||||
return {
|
||||
error: `Scrypted Cloud connection to ${SCRYPTED_SERVER} is disabled.`,
|
||||
};
|
||||
}
|
||||
|
||||
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}`
|
||||
},
|
||||
});
|
||||
this.console.log('registered', response.data);
|
||||
this.storageSettings.values.lastPersistedRegistrationId = registration_id;
|
||||
this.storageSettings.values.lastPersistedUpnpPort = upnp_port;
|
||||
this.storageSettings.values.registrationSecret = registration_secret;
|
||||
return response.data;
|
||||
if (!token_info) {
|
||||
const error = `Login to the Scrypted Cloud plugin to reach this server from the cloud, or disable this alert in the Scrypted Cloud plugin Connection settings.`;
|
||||
this.log.a(error);
|
||||
return {
|
||||
error,
|
||||
};
|
||||
}
|
||||
try {
|
||||
const response = await httpFetch({
|
||||
url: `https://${SCRYPTED_SERVER}/_punch/register?${q}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token_info}`
|
||||
},
|
||||
responseType: 'json',
|
||||
})
|
||||
const error = response.body?.error;
|
||||
if (error) {
|
||||
this.console.log('registration error', response.body);
|
||||
this.log.a(error);
|
||||
return response.body;
|
||||
}
|
||||
|
||||
this.console.log('registered', response.body);
|
||||
this.storageSettings.values.lastPersistedRegistrationId = registration_id;
|
||||
this.storageSettings.values.lastPersistedUpnpPort = authority.upnp_port;
|
||||
return response.body;
|
||||
}
|
||||
catch (e) {
|
||||
return {
|
||||
error: e.toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async setupCloudPush() {
|
||||
@@ -644,7 +721,18 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
return this.getSSLHostname() || SCRYPTED_SERVER;
|
||||
}
|
||||
|
||||
async convert(data: Buffer, fromMimeType: string): Promise<Buffer> {
|
||||
async convert(data: Buffer, fromMimeType: string, toMimeType: string): Promise<Buffer> {
|
||||
// if cloudflare is enabled and the plugin isn't set up as a custom domain, try to use the cloudflare url for
|
||||
// short lived urls.
|
||||
if (this.cloudflareTunnel && this.storageSettings.values.forwardingMode !== 'Custom Domain') {
|
||||
const params = new URLSearchParams(toMimeType.split(';')[1] || '');
|
||||
if (params.get('short-lived') === 'true') {
|
||||
const u = new URL(data.toString(), this.cloudflareTunnel);
|
||||
u.host = this.cloudflareTunnelHost;
|
||||
u.port = '';
|
||||
return Buffer.from(u.toString());
|
||||
}
|
||||
}
|
||||
return this.whitelist(data.toString(), 10 * 365 * 24 * 60 * 60 * 1000, `https://${this.getHostname()}`);
|
||||
}
|
||||
|
||||
@@ -677,9 +765,15 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
}
|
||||
|
||||
async getOauthUrl(): Promise<string> {
|
||||
const authority = this.getAuthority();
|
||||
|
||||
const args = qsstringify({
|
||||
hostname: os.hostname(),
|
||||
...authority,
|
||||
|
||||
registration_id: await this.manager.registrationId,
|
||||
registration_secret: this.storageSettings.values.registrationSecret,
|
||||
server_id: this.storageSettings.values.serverId,
|
||||
server_name: this.storageSettings.values.serverName,
|
||||
sender_id: DEFAULT_SENDER_ID,
|
||||
redirect_uri: `https://${SCRYPTED_SERVER}/web/oauth/callback`,
|
||||
})
|
||||
@@ -783,9 +877,6 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
await once(this.secureServer, 'listening');
|
||||
this.storageSettings.values.securePort = this.securePort = (this.secureServer.address() as any).port;
|
||||
|
||||
this.upnpInterval = setInterval(() => this.refreshPortForward(), 30 * 60 * 1000);
|
||||
this.refreshPortForward();
|
||||
|
||||
const agent = new http.Agent({ maxSockets: Number.MAX_VALUE, keepAlive: true });
|
||||
this.proxy = HttpProxy.createProxy({
|
||||
agent,
|
||||
@@ -824,16 +915,20 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
this.console.log('scrypted server requested a connection:', random);
|
||||
|
||||
const registrationId = await this.manager.registrationId;
|
||||
this.ensureReverseConnections(registrationId);
|
||||
|
||||
const client = tls.connect(4001, SCRYPTED_SERVER, {
|
||||
const { address } = message;
|
||||
const [serverHost, serverPort] = address?.split(':') || [SCRYPTED_SERVER, 4001];
|
||||
|
||||
this.ensureReverseConnections(registrationId, serverPort, serverHost);
|
||||
|
||||
const client = tls.connect(serverPort, serverHost, {
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
client.on('close', () => this.console.log('scrypted server connection ended:', random));
|
||||
client.write(registrationId + '\n');
|
||||
const mux: any = new bpmux.BPMux(client as any);
|
||||
mux.on('handshake', async (socket: Duplex) => {
|
||||
this.ensureReverseConnections(registrationId);
|
||||
this.ensureReverseConnections(registrationId, serverPort, serverHost);
|
||||
|
||||
this.console.warn('mux connection required');
|
||||
|
||||
@@ -885,10 +980,11 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
});
|
||||
const tmp = `${bin}.tmp`;
|
||||
|
||||
const stream = await axios('https://github.com/scryptedapp/cloudflared/releases/download/2023.8.2/cloudflared-darwin-arm64', {
|
||||
responseType: 'stream',
|
||||
const stream = await httpFetch({
|
||||
url: 'https://github.com/scryptedapp/cloudflared/releases/download/2023.8.2/cloudflared-darwin-arm64',
|
||||
responseType: 'readable',
|
||||
});
|
||||
const write = stream.data.pipe(fs.createWriteStream(tmp));
|
||||
const write = stream.body.pipe(fs.createWriteStream(tmp));
|
||||
await once(write, 'close');
|
||||
renameSync(tmp, bin);
|
||||
fs.chmodSync(bin, 0o0755)
|
||||
@@ -976,14 +1072,14 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
}
|
||||
}
|
||||
|
||||
ensureReverseConnections(registrationId: string) {
|
||||
ensureReverseConnections(registrationId: string, serverPort: number, serverHost: string) {
|
||||
while (this.reverseConnections.size < 10) {
|
||||
this.createReverseConnection(registrationId);
|
||||
this.createReverseConnection(registrationId, serverPort, serverHost);
|
||||
}
|
||||
}
|
||||
|
||||
async createReverseConnection(registrationId: string) {
|
||||
const client = tls.connect(4001, SCRYPTED_SERVER, {
|
||||
async createReverseConnection(registrationId: string, serverPort: number, serverHost: string) {
|
||||
const client = tls.connect(serverPort, serverHost, {
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
this.reverseConnections.add(client);
|
||||
@@ -994,7 +1090,7 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
this.reverseConnections.delete(client);
|
||||
|
||||
if (claimed)
|
||||
this.ensureReverseConnections(registrationId);
|
||||
this.ensureReverseConnections(registrationId, serverPort, serverHost);
|
||||
});
|
||||
client.write(`reverse:${registrationId}\n`);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user