mirror of
https://github.com/koush/scrypted.git
synced 2026-02-03 22:23:27 +00:00
Compare commits
618 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e0e9bc22a | ||
|
|
9e495c74d9 | ||
|
|
a9baeafe71 | ||
|
|
ee68fcd7d2 | ||
|
|
af6e18dc1a | ||
|
|
ddb8c7cf58 | ||
|
|
2be3c7f3df | ||
|
|
274f449e2f | ||
|
|
1109333e0f | ||
|
|
0349977a4d | ||
|
|
48548aafd5 | ||
|
|
ab70cce1b5 | ||
|
|
83fe0c2b7a | ||
|
|
77676a27c2 | ||
|
|
015dfab7a6 | ||
|
|
7f0f0cb6bd | ||
|
|
e49e13a167 | ||
|
|
9fd353236b | ||
|
|
e006d599d7 | ||
|
|
71cbe83a2a | ||
|
|
1438af8aea | ||
|
|
2237eb3221 | ||
|
|
7b56e86383 | ||
|
|
3653fb83d3 | ||
|
|
cd766a603e | ||
|
|
3648492299 | ||
|
|
88e8530677 | ||
|
|
325f84ca7e | ||
|
|
0a4b862fd8 | ||
|
|
e7d7fd6a00 | ||
|
|
f9dda8d1ca | ||
|
|
8b7decd077 | ||
|
|
9dd5e10eba | ||
|
|
475b833508 | ||
|
|
5f9006148a | ||
|
|
b77f1a55c1 | ||
|
|
6b9163e84e | ||
|
|
bc03bdd235 | ||
|
|
2592a7c228 | ||
|
|
0a4336879c | ||
|
|
e5cef3f217 | ||
|
|
d34396afbc | ||
|
|
2622fc9256 | ||
|
|
410b1a4813 | ||
|
|
403c742be3 | ||
|
|
50a471b78f | ||
|
|
9b7ead26e0 | ||
|
|
3127bc38cb | ||
|
|
fb8b1a893d | ||
|
|
779d8eaa42 | ||
|
|
5eab99866f | ||
|
|
e10a4f3c58 | ||
|
|
2585b1832e | ||
|
|
5e8e0d7773 | ||
|
|
7c17b478d7 | ||
|
|
9f5dd55c73 | ||
|
|
b6f400382d | ||
|
|
024b2166b8 | ||
|
|
b49771840e | ||
|
|
4001fc996f | ||
|
|
0d97010ca8 | ||
|
|
e243d99d12 | ||
|
|
86a91dfbe4 | ||
|
|
c86ae752e8 | ||
|
|
b7ca477b98 | ||
|
|
c37f8926b8 | ||
|
|
4b181a8ac9 | ||
|
|
b8439aaec3 | ||
|
|
77d0c33657 | ||
|
|
0b6d61a801 | ||
|
|
71a2d27cbd | ||
|
|
f8f79f5cc2 | ||
|
|
988f297e32 | ||
|
|
6e109d89e0 | ||
|
|
6ada4854bc | ||
|
|
bc5e89668f | ||
|
|
4c11def52b | ||
|
|
8890d307f4 | ||
|
|
9f8f562dcc | ||
|
|
2ce798c8c2 | ||
|
|
4271ef321f | ||
|
|
f976903a29 | ||
|
|
4ca63aadd5 | ||
|
|
6c932aec89 | ||
|
|
d7030c3dcf | ||
|
|
172ebf06de | ||
|
|
5f28c5a291 | ||
|
|
4c9ba5073e | ||
|
|
11d67f36be | ||
|
|
d38357ded9 | ||
|
|
f22e2ccfe7 | ||
|
|
e2b2f68477 | ||
|
|
57e87fbe8d | ||
|
|
31b05162fc | ||
|
|
c63efa0fca | ||
|
|
ce5255aa45 | ||
|
|
4692be1586 | ||
|
|
632d971dd5 | ||
|
|
2f17c85e99 | ||
|
|
9c6cdc9ac3 | ||
|
|
7007456bdd | ||
|
|
73fc738c0b | ||
|
|
abd1227fab | ||
|
|
7d2226df75 | ||
|
|
8f50415920 | ||
|
|
20ed523b30 | ||
|
|
effadb1437 | ||
|
|
07c7c91c63 | ||
|
|
878ddbdf1c | ||
|
|
d95e9c78ea | ||
|
|
49dc1d8f36 | ||
|
|
425e17a88b | ||
|
|
9bca6b0a94 | ||
|
|
3a62d9cd31 | ||
|
|
8f6bedd9d8 | ||
|
|
1c2a9d767f | ||
|
|
7ecee4298c | ||
|
|
4f1aad895f | ||
|
|
94667d2136 | ||
|
|
7d13055eae | ||
|
|
f90140dbd7 | ||
|
|
8b3a66b6ba | ||
|
|
8c03852cfb | ||
|
|
d795cd527d | ||
|
|
a24d986717 | ||
|
|
60ec304e68 | ||
|
|
6a9d498ff8 | ||
|
|
c60821043b | ||
|
|
e5a63dd992 | ||
|
|
f77ea922f2 | ||
|
|
1e8deeb638 | ||
|
|
a28ecb71e1 | ||
|
|
4067455396 | ||
|
|
9b828a6045 | ||
|
|
efce576c68 | ||
|
|
66b314f2aa | ||
|
|
d6ebc1fa85 | ||
|
|
8d756a26bd | ||
|
|
81c28b86d3 | ||
|
|
73f5e03774 | ||
|
|
cd078afcf9 | ||
|
|
6e393514cf | ||
|
|
4b62bceede | ||
|
|
fbbbdd8ab5 | ||
|
|
a0e28c0a28 | ||
|
|
ff28238422 | ||
|
|
4e9744360a | ||
|
|
7336fac8c4 | ||
|
|
6771d17829 | ||
|
|
62f1ca66f6 | ||
|
|
13cc562e68 | ||
|
|
aff1e86d6f | ||
|
|
c1f1e96109 | ||
|
|
a36b3066fe | ||
|
|
cadf10b505 | ||
|
|
ed541629b2 | ||
|
|
7d022548b9 | ||
|
|
9aa9bae3a3 | ||
|
|
7f29b05980 | ||
|
|
b89573e910 | ||
|
|
18426bcdc1 | ||
|
|
f562dd5362 | ||
|
|
1f1218a594 | ||
|
|
1aca97c2ae | ||
|
|
bd41410367 | ||
|
|
291d734a05 | ||
|
|
feec534b86 | ||
|
|
9ae7e6c0b5 | ||
|
|
a6f11d6d0c | ||
|
|
a15af8005b | ||
|
|
c13a3f252a | ||
|
|
0eaf9ef2d9 | ||
|
|
b9fc69347a | ||
|
|
f6e8a363ab | ||
|
|
a6d163ec5a | ||
|
|
2d62944ac1 | ||
|
|
b564553998 | ||
|
|
6e4fdb6e99 | ||
|
|
ca00983ecd | ||
|
|
36b8b9eeed | ||
|
|
fbd6937627 | ||
|
|
7c66826657 | ||
|
|
62c4a8b240 | ||
|
|
af860d840a | ||
|
|
42eb4fc80b | ||
|
|
5c965936e9 | ||
|
|
fe5cc59872 | ||
|
|
5d965ebfa7 | ||
|
|
b462249d93 | ||
|
|
29d8abed45 | ||
|
|
65cb13b0d1 | ||
|
|
522f8e9cba | ||
|
|
16199463ec | ||
|
|
220c010232 | ||
|
|
02238f99b2 | ||
|
|
1e53234cd6 | ||
|
|
824b7327a1 | ||
|
|
81d4a3f249 | ||
|
|
db1bd07b71 | ||
|
|
35026f6b5b | ||
|
|
9160efc2f7 | ||
|
|
6bc1e6a742 | ||
|
|
475e4a60d7 | ||
|
|
1f2edf1a12 | ||
|
|
b3db0aa78f | ||
|
|
0766d67a75 | ||
|
|
d2ac428916 | ||
|
|
945fb16bd6 | ||
|
|
711eb222ed | ||
|
|
19f8bfb74a | ||
|
|
08a8428d6e | ||
|
|
4feeeda904 | ||
|
|
753373a691 | ||
|
|
2f3529b822 | ||
|
|
2501d1460b | ||
|
|
e063637100 | ||
|
|
5ec0bf4bf3 | ||
|
|
0c05b59121 | ||
|
|
cbbfa0b525 | ||
|
|
28835b1ccc | ||
|
|
0585e7bbaf | ||
|
|
b2040ea2c8 | ||
|
|
2fd2151b4f | ||
|
|
4c7974519d | ||
|
|
d91c919558 | ||
|
|
7a297761bc | ||
|
|
c15e10e5cf | ||
|
|
3494106857 | ||
|
|
7d3dfb16f0 | ||
|
|
63fc223036 | ||
|
|
6736379858 | ||
|
|
7a811b2b22 | ||
|
|
dd5cb432c9 | ||
|
|
ab3a71ab49 | ||
|
|
b5c9382180 | ||
|
|
81682678ac | ||
|
|
dec184629e | ||
|
|
f33bb53138 | ||
|
|
2d3957e086 | ||
|
|
d16ed9e54f | ||
|
|
d7e8052498 | ||
|
|
48cd3830a5 | ||
|
|
ce138d1a17 | ||
|
|
7b4919fba9 | ||
|
|
0b3dee3a03 | ||
|
|
4cef09540b | ||
|
|
92583e568a | ||
|
|
67aaa08c31 | ||
|
|
2e9f618f6f | ||
|
|
bf4d39d6af | ||
|
|
c31e68f720 | ||
|
|
6d8b3c1ce7 | ||
|
|
106fef95b4 | ||
|
|
488d68ee1c | ||
|
|
f7e35fb1ee | ||
|
|
b1bf897bdb | ||
|
|
8eb533c220 | ||
|
|
f10cdfbced | ||
|
|
8f5e9e5a8c | ||
|
|
cc0283ef39 | ||
|
|
5c7b67c973 | ||
|
|
d1be0f1b4c | ||
|
|
55d58d1e44 | ||
|
|
d9dccf36a3 | ||
|
|
33477fdf80 | ||
|
|
e6ece3aa3e | ||
|
|
6a4126191b | ||
|
|
e9f999b911 | ||
|
|
1fef31a081 | ||
|
|
659f99c33d | ||
|
|
a9deff0046 | ||
|
|
7a56cefe2a | ||
|
|
a06c6e9568 | ||
|
|
56f127a203 | ||
|
|
2ffe67b2db | ||
|
|
44dc648398 | ||
|
|
7807cc4bc6 | ||
|
|
81fb690089 | ||
|
|
8b15617f6e | ||
|
|
fd8aa70352 | ||
|
|
be888d215d | ||
|
|
ce5f568a5d | ||
|
|
336220559f | ||
|
|
8014060a54 | ||
|
|
7f4c8997b9 | ||
|
|
9f73b92dbd | ||
|
|
381892fca6 | ||
|
|
a28df23032 | ||
|
|
dc5456d36f | ||
|
|
3a23e8ed26 | ||
|
|
e0db86cb41 | ||
|
|
37ccefebd1 | ||
|
|
0076c4827f | ||
|
|
c5c07d8169 | ||
|
|
2372acc796 | ||
|
|
6b9c3e4aa0 | ||
|
|
d5b652da8c | ||
|
|
2b9a0f082d | ||
|
|
b10b4d047e | ||
|
|
74cd23bd88 | ||
|
|
ef742bdb23 | ||
|
|
6f7fa54f24 | ||
|
|
d9a575cb5a | ||
|
|
29094afa4d | ||
|
|
62a92fe083 | ||
|
|
9b8bde556c | ||
|
|
326ef11760 | ||
|
|
92a0b4a863 | ||
|
|
9fd3641455 | ||
|
|
2918cf9ae1 | ||
|
|
6f004db859 | ||
|
|
367d741c5f | ||
|
|
8f83894e49 | ||
|
|
ea6e33d159 | ||
|
|
1b5565b5b2 | ||
|
|
19692d02c6 | ||
|
|
4179698c12 | ||
|
|
1eea3a87d0 | ||
|
|
ec89a77955 | ||
|
|
443158286e | ||
|
|
b168ca52c6 | ||
|
|
fe01d3a1ba | ||
|
|
18cad22627 | ||
|
|
c67c9a028c | ||
|
|
0cff8ad5ed | ||
|
|
0269959cf3 | ||
|
|
1b6de42eca | ||
|
|
39342d5d46 | ||
|
|
c4b5af46d0 | ||
|
|
a46235d095 | ||
|
|
848d490a66 | ||
|
|
87fbb95157 | ||
|
|
c036da9ae0 | ||
|
|
1688fcc126 | ||
|
|
99cae0ba31 | ||
|
|
a7b00b9e91 | ||
|
|
3f2a62c6f2 | ||
|
|
3fc318a370 | ||
|
|
aed8575aa0 | ||
|
|
2e28b50588 | ||
|
|
2e87cc380f | ||
|
|
1fdd2d4b01 | ||
|
|
53b23b2ca8 | ||
|
|
54016a9c78 | ||
|
|
d207a3b824 | ||
|
|
e72a74d008 | ||
|
|
d1b907e45b | ||
|
|
4a4c47ffe2 | ||
|
|
f6baf99935 | ||
|
|
b5cc138e2b | ||
|
|
40738a74cf | ||
|
|
d2b1f104ca | ||
|
|
6cb4f589c0 | ||
|
|
5cf2b26630 | ||
|
|
e7f16af04c | ||
|
|
6287b9deaa | ||
|
|
b9b5fdb712 | ||
|
|
c85af9c8a5 | ||
|
|
069f765507 | ||
|
|
0e587abc79 | ||
|
|
47770c0a8d | ||
|
|
82d1c3afe5 | ||
|
|
1c9b52ce4f | ||
|
|
adcd9fa537 | ||
|
|
91e2c2870b | ||
|
|
1fc892815d | ||
|
|
38ed1acc15 | ||
|
|
3bdc9ab930 | ||
|
|
bfa6346333 | ||
|
|
fcbb308cb8 | ||
|
|
f137edcc8c | ||
|
|
53e6f083b9 | ||
|
|
0f96fdb4bc | ||
|
|
96ea3f3b27 | ||
|
|
a31d6482af | ||
|
|
be16bf7858 | ||
|
|
1dad0126bc | ||
|
|
9292ebbe48 | ||
|
|
0b3a1a1998 | ||
|
|
b5d58b6899 | ||
|
|
215a56f70e | ||
|
|
c593701e72 | ||
|
|
46351f2fd7 | ||
|
|
9bce4acd14 | ||
|
|
cba20ec887 | ||
|
|
7c41516cce | ||
|
|
1f209072ba | ||
|
|
8978bff8a9 | ||
|
|
04c500b855 | ||
|
|
8b4859579c | ||
|
|
90deaf1161 | ||
|
|
de56a8c653 | ||
|
|
a5215ae92b | ||
|
|
73cd40b540 | ||
|
|
93556dd404 | ||
|
|
125b436cb6 | ||
|
|
0a4ea032f5 | ||
|
|
c658cee5c9 | ||
|
|
6589176c8b | ||
|
|
6c4c83f655 | ||
|
|
8d4124adda | ||
|
|
b7cda86df7 | ||
|
|
6622e13e51 | ||
|
|
cbc45da679 | ||
|
|
e7d06c66af | ||
|
|
ea02bc3b6f | ||
|
|
2b43cb7d15 | ||
|
|
f3c0362e18 | ||
|
|
817ae42250 | ||
|
|
8043f83f20 | ||
|
|
d33ab5dbcf | ||
|
|
2b1674bea8 | ||
|
|
f045e59258 | ||
|
|
9125aafc07 | ||
|
|
6f5244ec9f | ||
|
|
f1eb2f988a | ||
|
|
1f659d9a72 | ||
|
|
dd98f12f2a | ||
|
|
2063e3822a | ||
|
|
f7495a7a76 | ||
|
|
fddb9c655f | ||
|
|
297e7a7b4f | ||
|
|
29e080f6b6 | ||
|
|
c72ea24794 | ||
|
|
ada80796de | ||
|
|
1ebcf32998 | ||
|
|
79765ba58e | ||
|
|
ff4665520c | ||
|
|
be5b810335 | ||
|
|
fdc99b7fa6 | ||
|
|
f730d13cbd | ||
|
|
af02753cef | ||
|
|
9334d1c2a4 | ||
|
|
71ecc07e2b | ||
|
|
5310dd5ff6 | ||
|
|
adf1a10659 | ||
|
|
2ecc26c914 | ||
|
|
9a49416831 | ||
|
|
f0eff01898 | ||
|
|
edd071739f | ||
|
|
ab81c568bc | ||
|
|
62470df0af | ||
|
|
19b83eb056 | ||
|
|
b75d4cbfd4 | ||
|
|
8c0bb7b205 | ||
|
|
ef64515e56 | ||
|
|
302272e437 | ||
|
|
80e433f6ef | ||
|
|
60786aba2b | ||
|
|
256fde46f6 | ||
|
|
e1a7dd367e | ||
|
|
8612ba3462 | ||
|
|
ab638f26be | ||
|
|
02b881a2d2 | ||
|
|
35475b03e2 | ||
|
|
0b55c777f8 | ||
|
|
68f86d214c | ||
|
|
2abea2d25b | ||
|
|
1c2f17b9f9 | ||
|
|
e3d4800e4f | ||
|
|
d2f175715b | ||
|
|
93c1a699f1 | ||
|
|
41570e9134 | ||
|
|
3ef75854c2 | ||
|
|
c88a638f4e | ||
|
|
793c4da33a | ||
|
|
68f071660e | ||
|
|
8ea5b6aca6 | ||
|
|
2f13c77444 | ||
|
|
981ad183f5 | ||
|
|
8748be82ef | ||
|
|
a347fc2b73 | ||
|
|
002bf3b52c | ||
|
|
72abcd79ec | ||
|
|
86e5b824c7 | ||
|
|
43f6f176f0 | ||
|
|
bc543aa28e | ||
|
|
e90db378e8 | ||
|
|
f2907532aa | ||
|
|
866706505a | ||
|
|
59db3b622c | ||
|
|
7451b9903a | ||
|
|
aded2e43b1 | ||
|
|
031a7527e1 | ||
|
|
2aca568707 | ||
|
|
6b22d34831 | ||
|
|
429d9ec5a6 | ||
|
|
b426668146 | ||
|
|
8bce14f834 | ||
|
|
7511abf768 | ||
|
|
180c12e8cc | ||
|
|
1ed7d03a20 | ||
|
|
9e7b57f154 | ||
|
|
205fdb0222 | ||
|
|
d8f3edee1e | ||
|
|
90c9efc8a6 | ||
|
|
3893ccd776 | ||
|
|
1b154f14bc | ||
|
|
2e3eba4350 | ||
|
|
450f05910a | ||
|
|
22505c9226 | ||
|
|
7120bf86ff | ||
|
|
b49742204f | ||
|
|
6fda76a5e8 | ||
|
|
08bd785d45 | ||
|
|
aa9ddb35aa | ||
|
|
7997c07179 | ||
|
|
a67e24d5dc | ||
|
|
0d4da0dd06 | ||
|
|
993e903f3b | ||
|
|
fbb11a5312 | ||
|
|
ea72d2159b | ||
|
|
1892fdb529 | ||
|
|
1e16793b20 | ||
|
|
2f6c577b47 | ||
|
|
212306449b | ||
|
|
16445bc38e | ||
|
|
b6e9e15d4f | ||
|
|
39abd49ea0 | ||
|
|
05b9b49732 | ||
|
|
1857acac66 | ||
|
|
fedf184847 | ||
|
|
d2afac0dd6 | ||
|
|
6844b55983 | ||
|
|
379dabc182 | ||
|
|
df3c751f2d | ||
|
|
da714d1f94 | ||
|
|
34ee29b7b4 | ||
|
|
4c48f50e01 | ||
|
|
81a5a4349c | ||
|
|
8526c92dcc | ||
|
|
73fefeec26 | ||
|
|
6060b50856 | ||
|
|
d29cd7e421 | ||
|
|
8589283135 | ||
|
|
837dae5f02 | ||
|
|
c26aa2d01e | ||
|
|
c98eca23ab | ||
|
|
eb5d1ac4f6 | ||
|
|
37b0e46dd0 | ||
|
|
042dd84520 | ||
|
|
62d5c145c2 | ||
|
|
1ea3774849 | ||
|
|
9d8345e901 | ||
|
|
9ed850e327 | ||
|
|
957d27b8ef | ||
|
|
b74a957ecb | ||
|
|
debaedfd8c | ||
|
|
0123a97e3c | ||
|
|
a32d47e192 | ||
|
|
90ed8bd3f5 | ||
|
|
c4f4002f55 | ||
|
|
1ea2828e78 | ||
|
|
eb864456df | ||
|
|
51af4f07ff | ||
|
|
f6201acf2a | ||
|
|
96ac479c73 | ||
|
|
0c08875de3 | ||
|
|
bd05fc1b5d | ||
|
|
5a0d325718 | ||
|
|
015794c1d1 | ||
|
|
02d5b429b7 | ||
|
|
e169d154e7 | ||
|
|
01c7b5674a | ||
|
|
a7a1aed0dc | ||
|
|
6bb3f0fd19 | ||
|
|
7828de9d50 | ||
|
|
ea77bb29d0 | ||
|
|
bb184247d0 | ||
|
|
dbc45173ae | ||
|
|
95a23b2882 | ||
|
|
212883e84b | ||
|
|
1200537d62 | ||
|
|
5f6adc9449 | ||
|
|
7d17236ca7 | ||
|
|
028401362a | ||
|
|
69927be4f4 | ||
|
|
ffee1c5cc2 | ||
|
|
ebc3a03e2c | ||
|
|
4246e3c476 | ||
|
|
3fce0838f1 | ||
|
|
2609e301fe | ||
|
|
f4737bf2ac | ||
|
|
fc102aa526 | ||
|
|
9ef33e156f | ||
|
|
881865a0cb | ||
|
|
be5643cc53 | ||
|
|
7e6eba1596 | ||
|
|
27dde776a6 | ||
|
|
b24159a22a | ||
|
|
b6c242b9d5 | ||
|
|
2fbaa12caa | ||
|
|
eb5a497e82 | ||
|
|
66a0ea08ec | ||
|
|
0527baf14a | ||
|
|
c7c5c6eed5 | ||
|
|
143c950c19 | ||
|
|
8d0bb0fa97 | ||
|
|
964274e50c | ||
|
|
e9844528aa | ||
|
|
0609fc8986 | ||
|
|
9331b71433 | ||
|
|
21f8239db7 | ||
|
|
86042ec3fe | ||
|
|
cdb87fb268 | ||
|
|
63dcd35b17 | ||
|
|
951c3b9be6 | ||
|
|
ed642bb3fe | ||
|
|
8093cdd3d9 | ||
|
|
fcbfc3a73f | ||
|
|
94945a48bd | ||
|
|
e360ede5cb | ||
|
|
bc9ec73567 | ||
|
|
cd7e570508 | ||
|
|
1b06c9c11d | ||
|
|
154ab42d15 | ||
|
|
1929f6e8ed | ||
|
|
58bfa17cfe |
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is. The issue tracker is only for reporting bugs in Scrypted, for general support check Discord. Hardrware support requests or assistance requests will be immediately closed.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
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]
|
||||
|
||||
**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]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
50
.github/workflows/docker-HEAD.yml
vendored
50
.github/workflows/docker-HEAD.yml
vendored
@@ -1,50 +0,0 @@
|
||||
name: Publish Scrypted (git HEAD)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
push_to_registry:
|
||||
name: Push Docker image to Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: ["16-bullseye"]
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Login to Github Container Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image (scrypted)
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
build-args: BASE=${{ matrix.node }}
|
||||
context: .
|
||||
file: docker/Dockerfile.HEAD
|
||||
platforms: linux/amd64,linux/arm64,linux/armhf
|
||||
push: true
|
||||
tags: |
|
||||
koush/scrypted:HEAD
|
||||
ghcr.io/koush/scrypted:HEAD
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
60
.github/workflows/docker-common.yml
vendored
60
.github/workflows/docker-common.yml
vendored
@@ -2,54 +2,70 @@ name: Publish Scrypted Common
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
schedule:
|
||||
# publish the common base once a month.
|
||||
- cron: '30 8 2 * *'
|
||||
|
||||
jobs:
|
||||
push_to_registry:
|
||||
build:
|
||||
name: Push Docker image to Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: self-hosted
|
||||
# runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
NODE_VERSION: ["18"]
|
||||
BUILDPACK_DEPS_BASE: ["bullseye"]
|
||||
NODE_VERSION: ["18", "20"]
|
||||
BASE: ["jammy"]
|
||||
FLAVOR: ["full", "lite", "thin"]
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- 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 SSH
|
||||
uses: MrSquaare/ssh-setup-action@v2
|
||||
with:
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM7 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
platforms: linux/arm64,linux/armhf
|
||||
append: |
|
||||
- endpoint: ssh://${{ secrets.DOCKER_SSH_USER }}@${{ secrets.DOCKER_SSH_HOST_ARM64 }}
|
||||
platforms: linux/arm64
|
||||
- endpoint: ssh://${{ secrets.DOCKER_SSH_USER }}@${{ secrets.DOCKER_SSH_HOST_ARM7 }}
|
||||
platforms: linux/armhf
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Login to Github Container Registry
|
||||
uses: docker/login-action@v1
|
||||
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@v2
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
build-args: NODE_VERSION=${{ matrix.NODE_VERSION }}
|
||||
context: docker/
|
||||
file: docker/Dockerfile.${{ matrix.FLAVOR }}
|
||||
platforms: linux/amd64,linux/arm64,linux/armhf
|
||||
build-args: |
|
||||
NODE_VERSION=${{ matrix.NODE_VERSION }}
|
||||
BASE=${{ matrix.BASE }}
|
||||
context: install/docker/
|
||||
file: install/docker/Dockerfile.${{ matrix.FLAVOR }}
|
||||
platforms: linux/amd64,linux/armhf,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
koush/scrypted-common:${{ matrix.NODE_VERSION }}-${{ matrix.BUILDPACK_DEPS_BASE }}-${{ matrix.FLAVOR }}
|
||||
# ${{ matrix.NODE_VERSION == '16-bullseye' && 'koush/scrypted-common:latest' || '' }}
|
||||
koush/scrypted-common:${{ matrix.NODE_VERSION }}-${{ matrix.BASE }}-${{ matrix.FLAVOR }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
96
.github/workflows/docker.yml
vendored
96
.github/workflows/docker.yml
vendored
@@ -1,48 +1,66 @@
|
||||
name: Publish Scrypted
|
||||
name: Publish Scrypted Docker Image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
docker_tag:
|
||||
description: 'Docker Tag'
|
||||
tag:
|
||||
description: "The npm tag used to build the Docker image. The tag will be resolved as a specific version on npm, and that will be used to version the docker image."
|
||||
required: true
|
||||
package_version:
|
||||
description: 'Package Version'
|
||||
publish_tag:
|
||||
description: "The versioned tag for the published Docker image. NPM will use the minor version, Docker should only specify a patch version."
|
||||
required: false
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
push_to_registry:
|
||||
build:
|
||||
name: Push Docker image to Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: self-hosted
|
||||
# runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
BASE: ["18-bullseye-full", "18-bullseye-lite", "18-bullseye-thin"]
|
||||
BASE: ["18-jammy-full", "18-jammy-lite", "18-jammy-thin", "20-jammy-full", "20-jammy-lite", "20-jammy-thin"]
|
||||
SUPERVISOR: ["", ".s6"]
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: get-npm-version
|
||||
id: package-version
|
||||
uses: martinbeentjes/npm-get-version-action@master
|
||||
|
||||
- name: NPM Package Request
|
||||
id: npm-request
|
||||
uses: fjogeleit/http-request-action@v1
|
||||
with:
|
||||
path: server
|
||||
url: 'https://registry.npmjs.org/@scrypted/server'
|
||||
method: 'GET'
|
||||
|
||||
- name: Print Version
|
||||
run: echo "Version ${{ github.event.inputs.package_version || steps.package-version.outputs.current-version }}"
|
||||
|
||||
- name: Get current date
|
||||
id: date
|
||||
run: echo "::set-output name=date::$(date +'%Y-%m-%d')"
|
||||
- name: Set NPM Version
|
||||
id: package-version
|
||||
run: echo "NPM_VERSION=${{ fromJson(steps.npm-request.outputs.response)['dist-tags'][ github.event.inputs.tag] }}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- 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_ARM64 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up SSH
|
||||
uses: MrSquaare/ssh-setup-action@v2
|
||||
with:
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM7 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
with:
|
||||
platforms: linux/arm64,linux/armhf
|
||||
append: |
|
||||
- endpoint: ssh://${{ secrets.DOCKER_SSH_USER }}@${{ secrets.DOCKER_SSH_HOST_ARM64 }}
|
||||
platforms: linux/arm64
|
||||
- endpoint: ssh://${{ secrets.DOCKER_SSH_USER }}@${{ secrets.DOCKER_SSH_HOST_ARM7 }}
|
||||
platforms: linux/armhf
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
@@ -56,31 +74,31 @@ jobs:
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image (scrypted)
|
||||
uses: docker/build-push-action@v3
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
build-args: |
|
||||
BASE=${{ matrix.BASE }}
|
||||
SCRYPTED_INSTALL_VERSION=${{ github.event.inputs.package_version }}
|
||||
context: docker/
|
||||
file: docker/Dockerfile${{ matrix.SUPERVISOR }}
|
||||
SCRYPTED_INSTALL_VERSION=${{ steps.package-version.outputs.NPM_VERSION }}
|
||||
context: install/docker/
|
||||
file: install/docker/Dockerfile${{ matrix.SUPERVISOR }}
|
||||
platforms: linux/amd64,linux/arm64,linux/armhf
|
||||
push: true
|
||||
tags: |
|
||||
${{ format('koush/scrypted:{0}{1}-v{2}', matrix.BASE, matrix.SUPERVISOR, github.event.inputs.package_version || steps.package-version.outputs.current-version) }}
|
||||
${{ matrix.BASE == '18-bullseye-full' && matrix.SUPERVISOR == '.s6' && format('koush/scrypted:{0}', github.event.inputs.docker_tag) || '' }}
|
||||
${{ github.event.inputs.docker_tag == 'latest' && matrix.BASE == '18-bullseye-full' && matrix.SUPERVISOR == '' && 'koush/scrypted:full' || '' }}
|
||||
${{ github.event.inputs.docker_tag == 'latest' && matrix.BASE == '18-bullseye-lite' && matrix.SUPERVISOR == '' && 'koush/scrypted:lite' || '' }}
|
||||
${{ github.event.inputs.docker_tag == 'latest' && matrix.BASE == '18-bullseye-thin' && matrix.SUPERVISOR == '' && 'koush/scrypted:thin' || '' }}
|
||||
${{ github.event.inputs.docker_tag == 'latest' && matrix.BASE == '18-bullseye-lite' && matrix.SUPERVISOR == '.s6' && 'koush/scrypted:lite-s6' || '' }}
|
||||
${{ github.event.inputs.docker_tag == 'latest' && matrix.BASE == '18-bullseye-thin' && matrix.SUPERVISOR == '.s6' && 'koush/scrypted:thin-s6' || '' }}
|
||||
${{ 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('ghcr.io/koush/scrypted:{0}{1}-v{2}', matrix.BASE, matrix.SUPERVISOR, github.event.inputs.package_version || steps.package-version.outputs.current-version) }}
|
||||
${{ matrix.BASE == '18-bullseye-full' && matrix.SUPERVISOR == '.s6' && format('ghcr.io/koush/scrypted:{0}', github.event.inputs.docker_tag) || '' }}
|
||||
${{ github.event.inputs.docker_tag == 'latest' && matrix.BASE == '18-bullseye-full' && matrix.SUPERVISOR == '' && 'ghcr.io/koush/scrypted:full' || '' }}
|
||||
${{ github.event.inputs.docker_tag == 'latest' && matrix.BASE == '18-bullseye-lite' && matrix.SUPERVISOR == '' && 'ghcr.io/koush/scrypted:lite' || '' }}
|
||||
${{ github.event.inputs.docker_tag == 'latest' && matrix.BASE == '18-bullseye-thin' && matrix.SUPERVISOR == '' && 'ghcr.io/koush/scrypted:thin' || '' }}
|
||||
${{ github.event.inputs.docker_tag == 'latest' && matrix.BASE == '18-bullseye-lite' && matrix.SUPERVISOR == '.s6' && 'ghcr.io/koush/scrypted:lite-s6' || '' }}
|
||||
${{ github.event.inputs.docker_tag == 'latest' && matrix.BASE == '18-bullseye-thin' && matrix.SUPERVISOR == '.s6' && 'ghcr.io/koush/scrypted:thin-s6' || '' }}
|
||||
${{ 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' || '' }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
10
.github/workflows/test.yml
vendored
10
.github/workflows/test.yml
vendored
@@ -3,9 +3,9 @@ name: Test
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths: ["docker/**", ".github/workflows/test.yml"]
|
||||
paths: ["install/**", ".github/workflows/test.yml"]
|
||||
pull_request:
|
||||
paths: ["docker/**", ".github/workflows/test.yml"]
|
||||
paths: ["install/**", ".github/workflows/test.yml"]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
|
||||
- name: Run install script
|
||||
run: |
|
||||
cat ./docker/install-scrypted-dependencies-linux.sh | sudo SERVICE_USER=$USER bash
|
||||
cat ./install/local/install-scrypted-dependencies-linux.sh | sudo SERVICE_USER=$USER bash
|
||||
|
||||
- name: Test server is running
|
||||
run: |
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
- name: Run install script
|
||||
run: |
|
||||
mkdir -p ~/.scrypted
|
||||
bash ./docker/install-scrypted-dependencies-mac.sh
|
||||
bash ./install/local/install-scrypted-dependencies-mac.sh
|
||||
|
||||
- name: Test server is running
|
||||
run: |
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
|
||||
- name: Run install script
|
||||
run: |
|
||||
.\docker\install-scrypted-dependencies-win.ps1
|
||||
.\install\local\install-scrypted-dependencies-win.ps1
|
||||
|
||||
- name: Test server is running
|
||||
run: |
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,4 @@
|
||||
.DS_Store
|
||||
__pycache__
|
||||
venv
|
||||
.venv
|
||||
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -33,5 +33,5 @@
|
||||
path = plugins/sample-cameraprovider
|
||||
url = ../../koush/scrypted-sample-cameraprovider
|
||||
[submodule "plugins/cloud/node-nat-upnp"]
|
||||
path = plugins/cloud/node-nat-upnp
|
||||
path = plugins/cloud/external/node-nat-upnp
|
||||
url = ../../koush/node-nat-upnp.git
|
||||
|
||||
58
README.md
58
README.md
@@ -1,58 +1,20 @@
|
||||
# Scrypted
|
||||
|
||||
Scrypted is a high performance home video integration and automation platform.
|
||||
* Video load instantly, everywhere: [Demo](https://www.reddit.com/r/homebridge/comments/r34k6b/if_youre_using_homebridge_for_cameras_ditch_it/)
|
||||
* [HomeKit Secure Video Support](https://github.com/koush/scrypted/wiki/HomeKit-Secure-Video-Setup)
|
||||
* Google Home support: "Ok Google, Stream Backyard"
|
||||
* Alexa Support: Streaming to Alexa app on iOS/Android and Echo Show.
|
||||
Scrypted is a high performance home video integration platform and NVR with smart detections. [Instant, low latency, streaming](https://streamable.com/xbxn7z) to HomeKit, Google Home, and Alexa. Supports most cameras. [Learn more](https://docs.scrypted.app).
|
||||
|
||||
<img width="400" alt="Scrypted_Management_Console" src="https://user-images.githubusercontent.com/73924/185666320-ae972867-6c2c-488a-8413-fd8a215e9fee.png">
|
||||
<img src="https://github.com/koush/scrypted/assets/73924/57e1d556-cd3d-4448-81f9-a6c51b6513de">
|
||||
|
||||
# Installation
|
||||
## Installation and Documentation
|
||||
|
||||
Select the appropriate guide. After installation is finished, remember to visit [HomeKit Secure Video Setup](https://github.com/koush/scrypted/wiki/HomeKit-Secure-Video-Setup).
|
||||
Installation and camera onboarding instructions can be found in the [docs](https://docs.scrypted.app).
|
||||
|
||||
* [Raspberry Pi](https://github.com/koush/scrypted/wiki/Installation:-Raspberry-Pi)
|
||||
* Linux
|
||||
* [Docker Compose](https://github.com/koush/scrypted/wiki/Installation:-Docker-Compose-Linux) - This is the recommended method. Local installation may interfere with other server software.
|
||||
* [Docker](https://github.com/koush/scrypted/wiki/Installation:-Docker-Linux) - Use Docker Compose. This is a reference documentation.
|
||||
* [Local Installation](https://github.com/koush/scrypted/wiki/Installation:-Linux) - Use this if Docker scares you or whatever.
|
||||
* Mac
|
||||
* [Local Installation](https://github.com/koush/scrypted/wiki/Installation:-Mac)
|
||||
<!-- * Docker Desktop is [not supported](https://github.com/koush/scrypted/wiki/Installation:-Docker-Desktop). -->
|
||||
* Windows
|
||||
* [Local Installation](https://github.com/koush/scrypted/wiki/Installation:-Windows)
|
||||
* [WSL2 Installation](https://github.com/koush/scrypted/wiki/Installation:-WSL2-Windows)
|
||||
<!-- * Docker Desktop is [not supported](https://github.com/koush/scrypted/wiki/Installation:-Docker-Desktop). -->
|
||||
* [ReadyNAS: Docker](https://github.com/koush/scrypted/wiki/Installation:-Docker-ReadyNAS)
|
||||
* [Synology: Docker](https://github.com/koush/scrypted/wiki/Installation:-Docker-Synology-NAS)
|
||||
* [QNAP: Docker](https://github.com/koush/scrypted/wiki/Installation:-Docker-QNAP-NAS)
|
||||
* [Unraid: Docker](https://github.com/koush/scrypted/wiki/Installation:-Docker-Unraid)
|
||||
|
||||
## Discord
|
||||
|
||||
Chat on Discord for support, tips, announcements, and bug reporting. There is an active and helpful community.
|
||||
|
||||
[Join Scrypted Discord](https://discord.gg/DcFzmBHYGq)
|
||||
|
||||
## Wiki
|
||||
|
||||
There are many topics covered in the [Scrypted Wiki](https://github.com/koush/scrypted/wiki) sidebar. Review them for documented support, tips, and guides before asking for assistance on GitHub or Discord.
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
* Google Home
|
||||
* Apple HomeKit
|
||||
* Amazon Alexa
|
||||
|
||||
Supported accessories:
|
||||
* Camera and Core Plugins: https://github.com/koush/scrypted/tree/main/plugins
|
||||
* Community Plugins: https://github.com/orgs/scryptedapp/repositories
|
||||
## Community
|
||||
|
||||
Scrypted has active communities on [Discord](https://discord.gg/DcFzmBHYGq), [Reddit](https://reddit.com/r/scrypted), and [Github](https://github.com/koush/scrypted). Check them out if you have questions!
|
||||
|
||||
## Development
|
||||
|
||||
## Debug Scrypted Plugins in VSCode
|
||||
## Debug Scrypted Plugins in VS Code
|
||||
|
||||
```sh
|
||||
# this is an example for homekit.
|
||||
@@ -65,7 +27,7 @@ cd scrypted
|
||||
code plugins/homekit
|
||||
```
|
||||
|
||||
You can now launch (using the Start Debugging play button) the HomeKit Plugin in VSCode. Please be aware that you do *not* need to restart the Scrypted Server if you make changes to a plugin. Edit the plugin, launch, and the updated plugin will deploy on the running server.
|
||||
You can now launch (using the Start Debugging play button) the HomeKit Plugin in VS Code. Please be aware that you do *not* need to restart the Scrypted Server if you make changes to a plugin. Edit the plugin, launch, and the updated plugin will deploy on the running server.
|
||||
|
||||
If you do not want to set up VS Code, you can also run build and install the plugin directly from the command line:
|
||||
|
||||
@@ -79,7 +41,7 @@ npm run build && npm run scrypted-deploy 127.0.0.1
|
||||
Want to write your own plugin? Full documentation is available here: https://developer.scrypted.app
|
||||
|
||||
|
||||
## Debug the Scrypted Server in VSCode
|
||||
## Debug the Scrypted Server in VS Code
|
||||
|
||||
Debugging the server should not be necessary, as the server only provides the hosting and RPC mechanism for plugins. The following is for reference purpose. Most development can be done by debugging the relevant plugin.
|
||||
|
||||
@@ -93,4 +55,4 @@ cd scrypted
|
||||
code server
|
||||
```
|
||||
|
||||
You can now launch the Scrypted Server in VSCode.
|
||||
You can now launch the Scrypted Server in VS Code.
|
||||
|
||||
4
common/package-lock.json
generated
4
common/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/common",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/common",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.2",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/sdk": "file:../sdk",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/common",
|
||||
"private": true,
|
||||
"version": "1.0.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
|
||||
171
common/src/async-queue.ts
Normal file
171
common/src/async-queue.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
import { Deferred } from "./deferred";
|
||||
|
||||
class EndError extends Error {
|
||||
}
|
||||
|
||||
export function createAsyncQueue<T>() {
|
||||
let ended: Error | undefined;
|
||||
const waiting: Deferred<T>[] = [];
|
||||
const queued: { item: T, dequeued?: Deferred<void> }[] = [];
|
||||
|
||||
const dequeue = async () => {
|
||||
if (queued.length) {
|
||||
const { item, dequeued: enqueue } = queued.shift()!;
|
||||
enqueue?.resolve();
|
||||
return item;
|
||||
}
|
||||
|
||||
if (ended)
|
||||
throw ended;
|
||||
|
||||
const deferred = new Deferred<T>();
|
||||
waiting.push(deferred);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
const submit = (item: T, dequeued?: Deferred<void>, signal?: AbortSignal) => {
|
||||
if (ended)
|
||||
return false;
|
||||
|
||||
if (waiting.length) {
|
||||
const deferred = waiting.shift();
|
||||
dequeued?.resolve();
|
||||
deferred.resolve(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
const qi = {
|
||||
item,
|
||||
dequeued,
|
||||
};
|
||||
queued!.push(qi);
|
||||
|
||||
signal?.addEventListener('abort', () => {
|
||||
const index = queued.indexOf(qi);
|
||||
if (index === -1)
|
||||
return;
|
||||
queued.splice(index, 1);
|
||||
dequeued?.reject(new Error('abort'));
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function queue() {
|
||||
return (async function* () {
|
||||
while (true) {
|
||||
try {
|
||||
const item = await dequeue();
|
||||
yield item;
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof EndError)
|
||||
return;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
function clear(error?: Error) {
|
||||
const ret: T[] = [];
|
||||
const items = queued.splice(0, queued.length);
|
||||
for (const item of items) {
|
||||
if (error)
|
||||
item.dequeued?.reject(error)
|
||||
else
|
||||
item.dequeued?.resolve(undefined);
|
||||
ret.push(item.item);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return {
|
||||
clear() {
|
||||
return clear();
|
||||
},
|
||||
queued,
|
||||
async pipe(callback: (i: T) => void) {
|
||||
for await (const i of queue()) {
|
||||
callback(i as any);
|
||||
}
|
||||
},
|
||||
submit(item: T, signal?: AbortSignal) {
|
||||
return submit(item, undefined, signal);
|
||||
},
|
||||
end(e?: Error) {
|
||||
if (ended)
|
||||
return false;
|
||||
// catch to prevent unhandled rejection.
|
||||
ended = e || new EndError()
|
||||
clear(e);
|
||||
return true;
|
||||
},
|
||||
async enqueue(item: T, signal?: AbortSignal) {
|
||||
const dequeued = new Deferred<void>();
|
||||
if (!submit(item, dequeued, signal))
|
||||
return false;
|
||||
await dequeued.promise;
|
||||
return true;
|
||||
},
|
||||
dequeue,
|
||||
get queue() {
|
||||
return queue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// async function testSlowEnqueue() {
|
||||
// const asyncQueue = createAsyncQueue<number>();
|
||||
|
||||
// asyncQueue.submit(-1);
|
||||
// asyncQueue.submit(-1);
|
||||
// asyncQueue.submit(-1);
|
||||
// asyncQueue.submit(-1);
|
||||
|
||||
// (async () => {
|
||||
// console.log('go');
|
||||
// for (let i = 0; i < 10; i++) {
|
||||
// asyncQueue.submit(i);
|
||||
// await sleep(100);
|
||||
// }
|
||||
// asyncQueue.end(new Error('fail'));
|
||||
// })();
|
||||
|
||||
|
||||
// const runQueue = async (str?: string) => {
|
||||
// for await (const n of asyncQueue.queue) {
|
||||
// console.log(str, n);
|
||||
// }
|
||||
// }
|
||||
|
||||
// runQueue('start');
|
||||
|
||||
// setTimeout(runQueue, 400);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// async function testSlowDequeue() {
|
||||
// const asyncQueue = createAsyncQueue<number>();
|
||||
|
||||
// const runQueue = async (str?: string) => {
|
||||
// for await (const n of asyncQueue.queue) {
|
||||
// await sleep(100);
|
||||
// }
|
||||
// }
|
||||
|
||||
// runQueue()
|
||||
// .catch(e => console.error('queue threw', e));
|
||||
|
||||
// console.log('go');
|
||||
// for (let i = 0; i < 10; i++) {
|
||||
// console.log(await asyncQueue.enqueue(i));
|
||||
// console.log(i);
|
||||
// }
|
||||
// asyncQueue.end(new Error('fail'));
|
||||
// console.log(await asyncQueue.enqueue(555));
|
||||
// }
|
||||
|
||||
// testSlowDequeue();
|
||||
@@ -3,14 +3,13 @@ import sdk from "@scrypted/sdk";
|
||||
|
||||
const { systemManager } = sdk;
|
||||
|
||||
const autoIncludeToken = 'v4';
|
||||
|
||||
export abstract class AutoenableMixinProvider extends ScryptedDeviceBase {
|
||||
hasEnabledMixin: { [id: string]: string } = {};
|
||||
pluginsComponent: Promise<any>;
|
||||
unshiftMixin = false;
|
||||
|
||||
constructor(nativeId?: string) {
|
||||
constructor(nativeId?: string, public autoIncludeToken = 'v4') {
|
||||
super(nativeId);
|
||||
|
||||
try {
|
||||
@@ -30,10 +29,12 @@ export abstract class AutoenableMixinProvider extends ScryptedDeviceBase {
|
||||
this.maybeEnableMixin(eventSource);
|
||||
});
|
||||
|
||||
for (const id of Object.keys(systemManager.getSystemState())) {
|
||||
const device = systemManager.getDeviceById(id);
|
||||
this.maybeEnableMixin(device);
|
||||
}
|
||||
process.nextTick(() => {
|
||||
for (const id of Object.keys(systemManager.getSystemState())) {
|
||||
const device = systemManager.getDeviceById(id);
|
||||
this.maybeEnableMixin(device);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async shouldEnableMixin(device: ScryptedDevice) {
|
||||
@@ -44,7 +45,7 @@ export abstract class AutoenableMixinProvider extends ScryptedDeviceBase {
|
||||
if (!device || device.mixins?.includes(this.id))
|
||||
return;
|
||||
|
||||
if (this.hasEnabledMixin[device.id] === autoIncludeToken)
|
||||
if (this.hasEnabledMixin[device.id] === this.autoIncludeToken)
|
||||
return;
|
||||
|
||||
const match = await this.canMixin(device.type, device.interfaces);
|
||||
@@ -66,9 +67,9 @@ export abstract class AutoenableMixinProvider extends ScryptedDeviceBase {
|
||||
}
|
||||
|
||||
setHasEnabledMixin(id: string) {
|
||||
if (this.hasEnabledMixin[id] === autoIncludeToken)
|
||||
if (this.hasEnabledMixin[id] === this.autoIncludeToken)
|
||||
return;
|
||||
this.hasEnabledMixin[id] = autoIncludeToken;
|
||||
this.hasEnabledMixin[id] = this.autoIncludeToken;
|
||||
this.storage.setItem('hasEnabledMixin', JSON.stringify(this.hasEnabledMixin));
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,11 @@ import { EventEmitter } from 'events';
|
||||
import { Server } from 'net';
|
||||
import { Duplex } from 'stream';
|
||||
import { cloneDeep } from './clone-deep';
|
||||
import { Deferred } from "./deferred";
|
||||
import { listenZeroSingleClient } from './listen-cluster';
|
||||
import { ffmpegLogInitialOutput, safeKillFFmpeg, safePrintFFmpegArguments } from './media-helpers';
|
||||
import { createRtspParser } from "./rtsp-server";
|
||||
import { parseSdp } from "./sdp-utils";
|
||||
import { StreamChunk, StreamParser } from './stream-parser';
|
||||
|
||||
const { mediaManager } = sdk;
|
||||
@@ -57,9 +60,13 @@ export async function parseResolution(cp: ChildProcess) {
|
||||
}
|
||||
|
||||
async function parseInputToken(cp: ChildProcess, token: string) {
|
||||
let processed = 0;
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
cp.on('exit', () => reject(new Error('ffmpeg exited while waiting to parse stream information: ' + token)));
|
||||
const parser = (data: Buffer) => {
|
||||
processed += data.length;
|
||||
if (processed > 10000)
|
||||
return resolve(undefined);
|
||||
const stdout: string = data.toString().split('Output ')[0];
|
||||
const idx = stdout.lastIndexOf(`${token}: `);
|
||||
if (idx !== -1) {
|
||||
@@ -77,7 +84,11 @@ async function parseInputToken(cp: ChildProcess, token: string) {
|
||||
};
|
||||
cp.stdout.on('data', parser);
|
||||
cp.stderr.on('data', parser);
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
cp.stdout.removeAllListeners('data');
|
||||
cp.stderr.removeAllListeners('data');
|
||||
});
|
||||
}
|
||||
|
||||
export async function parseVideoCodec(cp: ChildProcess) {
|
||||
@@ -158,8 +169,6 @@ export async function startParserSession<T extends string>(ffmpegInput: FFmpegIn
|
||||
|
||||
const args = ffmpegInput.inputArguments.slice();
|
||||
|
||||
let needSdp = false;
|
||||
|
||||
const ensureActive = (killed: () => void) => {
|
||||
if (!isActive) {
|
||||
killed();
|
||||
@@ -211,11 +220,6 @@ export async function startParserSession<T extends string>(ffmpegInput: FFmpegIn
|
||||
}
|
||||
}
|
||||
|
||||
if (needSdp) {
|
||||
args.push('-sdp_file', `pipe:${pipeCount++}`);
|
||||
stdio.push('pipe');
|
||||
}
|
||||
|
||||
// start ffmpeg process with child process pipes
|
||||
args.unshift('-hide_banner');
|
||||
safePrintFFmpegArguments(console, args);
|
||||
@@ -225,20 +229,7 @@ export async function startParserSession<T extends string>(ffmpegInput: FFmpegIn
|
||||
ffmpegLogInitialOutput(console, cp, undefined, options?.storage);
|
||||
cp.on('exit', () => kill(new Error('ffmpeg exited')));
|
||||
|
||||
let sdp: Promise<Buffer[]>;
|
||||
if (needSdp) {
|
||||
sdp = new Promise<Buffer[]>(resolve => {
|
||||
const ret: Buffer[] = [];
|
||||
cp.stdio[pipeCount - 1].on('data', buffer => {
|
||||
ret.push(buffer);
|
||||
resolve(ret);
|
||||
});
|
||||
})
|
||||
}
|
||||
else {
|
||||
sdp = Promise.resolve([]);
|
||||
}
|
||||
|
||||
const deferredStart = new Deferred<void>();
|
||||
// now parse the created pipes
|
||||
const start = () => {
|
||||
for (const p of startParsers) {
|
||||
@@ -257,6 +248,7 @@ export async function startParserSession<T extends string>(ffmpegInput: FFmpegIn
|
||||
const { resetActivityTimer } = setupActivityTimer(container, kill, events, options?.timeout);
|
||||
|
||||
for await (const chunk of parser.parse(pipe as any, parseInt(inputVideoResolution?.[2]), parseInt(inputVideoResolution?.[3]))) {
|
||||
await deferredStart.promise;
|
||||
events.emit(container, chunk);
|
||||
resetActivityTimer();
|
||||
}
|
||||
@@ -268,14 +260,26 @@ export async function startParserSession<T extends string>(ffmpegInput: FFmpegIn
|
||||
});
|
||||
};
|
||||
|
||||
// tbh parsing stdout is super sketchy way of doing this.
|
||||
parseAudioCodec(cp).then(result => inputAudioCodec = result);
|
||||
parseResolution(cp).then(result => inputVideoResolution = result);
|
||||
await parseVideoCodec(cp).then(result => inputVideoCodec = result);
|
||||
const rtsp = (options.parsers as any).rtsp as ReturnType<typeof createRtspParser>;
|
||||
rtsp.sdp.then(sdp => {
|
||||
const parsed = parseSdp(sdp);
|
||||
const audio = parsed.msections.find(msection => msection.type === 'audio');
|
||||
const video = parsed.msections.find(msection => msection.type === 'video');
|
||||
inputVideoCodec = video?.codec;
|
||||
inputAudioCodec = audio?.codec;
|
||||
});
|
||||
|
||||
const sdp = new Deferred<Buffer[]>();
|
||||
rtsp.sdp.then(r => sdp.resolve([Buffer.from(r)]));
|
||||
killed.then(() => sdp.reject(new Error("ffmpeg killed before sdp could be parsed")));
|
||||
|
||||
start();
|
||||
|
||||
return {
|
||||
start,
|
||||
sdp,
|
||||
start() {
|
||||
deferredStart.resolve();
|
||||
},
|
||||
sdp: sdp.promise,
|
||||
get inputAudioCodec() {
|
||||
return inputAudioCodec;
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import net from 'net';
|
||||
import { once } from 'events';
|
||||
import dgram, { SocketType } from 'dgram';
|
||||
import { once } from 'events';
|
||||
import net from 'net';
|
||||
|
||||
export async function closeQuiet(socket: dgram.Socket | net.Server) {
|
||||
if (!socket)
|
||||
@@ -37,6 +37,23 @@ export async function createBindZero(socketType?: SocketType) {
|
||||
return createBindUdp(0, socketType);
|
||||
}
|
||||
|
||||
export async function createSquentialBindZero(socketType?: SocketType) {
|
||||
let attempts = 0;
|
||||
while (true) {
|
||||
const rtpServer = await createBindZero(socketType);
|
||||
try {
|
||||
const rtcpServer = await createBindUdp(rtpServer.port + 1, socketType);
|
||||
return [rtpServer, rtcpServer];
|
||||
}
|
||||
catch (e) {
|
||||
attempts++;
|
||||
closeQuiet(rtpServer.server);
|
||||
}
|
||||
if (attempts === 10)
|
||||
throw new Error('unable to reserve sequential udp ports')
|
||||
}
|
||||
}
|
||||
|
||||
export async function reserveUdpPort() {
|
||||
const udp = await createBindZero();
|
||||
await new Promise(resolve => udp.server.close(() => resolve(undefined)));
|
||||
@@ -62,4 +79,4 @@ export async function bind(server: dgram.Socket, port: number) {
|
||||
}
|
||||
}
|
||||
|
||||
export { listenZero, listenZeroSingleClient, ListenZeroSingleClientTimeoutError } from "@scrypted/server/src/listen-zero";
|
||||
export { ListenZeroSingleClientTimeoutError, listenZero, listenZeroSingleClient } from "@scrypted/server/src/listen-zero";
|
||||
|
||||
@@ -51,14 +51,8 @@ function silence() {
|
||||
return ret;
|
||||
}
|
||||
|
||||
export class BrowserSignalingSession implements RTCSignalingSession {
|
||||
private pc: RTCPeerConnection;
|
||||
pcDeferred = new Deferred<RTCPeerConnection>();
|
||||
dcDeferred = new Deferred<RTCDataChannel>();
|
||||
microphone: RTCRtpSender;
|
||||
micEnabled = false;
|
||||
onPeerConnection: (pc: RTCPeerConnection) => Promise<void>;
|
||||
options: RTCSignalingOptions = {
|
||||
function createOptions() {
|
||||
const options: RTCSignalingOptions = {
|
||||
userAgent: getUserAgent(),
|
||||
capabilities: {
|
||||
audio: RTCRtpReceiver.getCapabilities?.('audio') || {
|
||||
@@ -76,6 +70,18 @@ export class BrowserSignalingSession implements RTCSignalingSession {
|
||||
height: screen.height,
|
||||
},
|
||||
};
|
||||
return options;
|
||||
}
|
||||
|
||||
export class BrowserSignalingSession implements RTCSignalingSession {
|
||||
private pc: RTCPeerConnection;
|
||||
pcDeferred = new Deferred<RTCPeerConnection>();
|
||||
dcDeferred = new Deferred<RTCDataChannel>();
|
||||
microphone: RTCRtpSender;
|
||||
micEnabled = false;
|
||||
onPeerConnection: (pc: RTCPeerConnection) => Promise<void>;
|
||||
__proxy_props = { options: createOptions() };
|
||||
options = createOptions();
|
||||
|
||||
constructor() {
|
||||
}
|
||||
@@ -284,6 +290,10 @@ function createCandidateQueue(console: Console, type: string, session: RTCSignal
|
||||
}
|
||||
}
|
||||
|
||||
export async function legacyGetSignalingSessionOptions(session: RTCSignalingSession) {
|
||||
return typeof session.options === 'object' ? session.options : await session.getOptions();
|
||||
}
|
||||
|
||||
export async function connectRTCSignalingClients(
|
||||
console: Console,
|
||||
offerClient: RTCSignalingSession,
|
||||
@@ -291,8 +301,8 @@ export async function connectRTCSignalingClients(
|
||||
answerClient: RTCSignalingSession,
|
||||
answerSetup: Partial<RTCAVSignalingSetup>
|
||||
) {
|
||||
const offerOptions = await offerClient.getOptions();
|
||||
const answerOptions = await answerClient.getOptions();
|
||||
const offerOptions = await legacyGetSignalingSessionOptions(offerClient);
|
||||
const answerOptions = await legacyGetSignalingSessionOptions(answerClient);
|
||||
const disableTrickle = offerOptions?.disableTrickle || answerOptions?.disableTrickle;
|
||||
|
||||
if (offerOptions?.offer && answerOptions?.offer)
|
||||
|
||||
@@ -6,14 +6,14 @@ import { parseHTTPHeadersQuotedKeyValueSet } from 'http-auth-utils/dist/utils';
|
||||
import net from 'net';
|
||||
import { Duplex, Readable, Writable } from 'stream';
|
||||
import tls from 'tls';
|
||||
import { URL } from 'url';
|
||||
import { Deferred } from './deferred';
|
||||
import { closeQuiet, createBindUdp, createBindZero, listenZeroSingleClient } from './listen-cluster';
|
||||
import { closeQuiet, createBindZero, createSquentialBindZero, listenZeroSingleClient } from './listen-cluster';
|
||||
import { timeoutPromise } from './promise-utils';
|
||||
import { readLength, readLine } from './read-stream';
|
||||
import { MSection, parseSdp } from './sdp-utils';
|
||||
import { sleep } from './sleep';
|
||||
import { StreamChunk, StreamParser, StreamParserOptions } from './stream-parser';
|
||||
import { URL } from 'url';
|
||||
|
||||
const REQUIRED_WWW_AUTHENTICATE_KEYS = ['realm', 'nonce'];
|
||||
|
||||
@@ -195,48 +195,17 @@ export function createRtspParser(options?: StreamParserOptions): RtspStreamParse
|
||||
'-f', 'rtsp',
|
||||
],
|
||||
findSyncFrame(streamChunks: StreamChunk[]) {
|
||||
let foundIndex: number;
|
||||
let nonVideo: {
|
||||
[codec: string]: StreamChunk,
|
||||
} = {};
|
||||
|
||||
const createSyncFrame = () => {
|
||||
const ret = streamChunks.slice(foundIndex);
|
||||
// for (const nv of Object.values(nonVideo)) {
|
||||
// ret.unshift(nv);
|
||||
// }
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (let prebufferIndex = 0; prebufferIndex < streamChunks.length; prebufferIndex++) {
|
||||
const streamChunk = streamChunks[prebufferIndex];
|
||||
if (streamChunk.type !== 'h264') {
|
||||
nonVideo[streamChunk.type] = streamChunk;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (findH264NaluType(streamChunk, H264_NAL_TYPE_SPS))
|
||||
foundIndex = prebufferIndex;
|
||||
}
|
||||
|
||||
if (foundIndex !== undefined)
|
||||
return createSyncFrame();
|
||||
|
||||
nonVideo = {};
|
||||
// some streams don't contain codec info, so find an idr frame instead.
|
||||
for (let prebufferIndex = 0; prebufferIndex < streamChunks.length; prebufferIndex++) {
|
||||
const streamChunk = streamChunks[prebufferIndex];
|
||||
if (streamChunk.type !== 'h264') {
|
||||
nonVideo[streamChunk.type] = streamChunk;
|
||||
continue;
|
||||
if (findH264NaluType(streamChunk, H264_NAL_TYPE_SPS) || findH264NaluType(streamChunk, H264_NAL_TYPE_IDR)) {
|
||||
return streamChunks.slice(prebufferIndex);
|
||||
}
|
||||
if (findH264NaluType(streamChunk, H264_NAL_TYPE_IDR))
|
||||
foundIndex = prebufferIndex;
|
||||
}
|
||||
|
||||
if (foundIndex !== undefined)
|
||||
return createSyncFrame();
|
||||
|
||||
// oh well!
|
||||
},
|
||||
sdp: new Promise<string>(r => resolve = r),
|
||||
@@ -964,8 +933,7 @@ export class RtspServer {
|
||||
const match = transport.match(/.*?client_port=([0-9]+)-([0-9]+)/);
|
||||
const [_, rtp, rtcp] = match;
|
||||
|
||||
const rtpServer = await createBindZero();
|
||||
const rtcpServer = await createBindUdp(rtpServer.port + 1);
|
||||
const [rtpServer, rtcpServer] = await createSquentialBindZero();
|
||||
this.client.on('close', () => closeQuiet(rtpServer.server));
|
||||
this.client.on('close', () => closeQuiet(rtcpServer.server));
|
||||
this.setupTracks[msection.control] = {
|
||||
|
||||
@@ -217,14 +217,12 @@ const acontrol = 'a=control:';
|
||||
const artpmap = 'a=rtpmap:';
|
||||
export function parseMSection(msection: string[]) {
|
||||
const control = msection.find(line => line.startsWith(acontrol))?.substring(acontrol.length);
|
||||
const rtpmapFirst = msection.find(line => line.startsWith(artpmap));
|
||||
const mline = parseMLine(msection[0]);
|
||||
|
||||
let codec = parseRtpMap(mline.type, rtpmapFirst).codec;
|
||||
|
||||
const rtpmaps = msection.filter(line => line.startsWith(artpmap)).map(line => parseRtpMap(mline.type, line));
|
||||
|
||||
const rawRtpmaps = msection.filter(line => line.startsWith(artpmap));
|
||||
const rtpmaps = rawRtpmaps.map(line => parseRtpMap(mline.type, line));
|
||||
const codec = parseRtpMap(mline.type, rawRtpmaps[0]).codec;
|
||||
let direction: string;
|
||||
|
||||
for (const checkDirection of ['sendonly', 'sendrecv', 'recvonly', 'inactive']) {
|
||||
const found = msection.find(line => line === 'a=' + checkDirection);
|
||||
if (found) {
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
ARG BUILDPACK_DEPS_BASE="bullseye"
|
||||
FROM debian:${BUILDPACK_DEPS_BASE} as header
|
||||
|
||||
RUN apt-get update && apt-get -y install curl wget
|
||||
|
||||
# switch to nvm?
|
||||
ARG NODE_VERSION=18
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash -
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y nodejs
|
||||
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install software-properties-common apt-utils
|
||||
RUN apt-get -y update
|
||||
|
||||
# base development stuff
|
||||
RUN apt-get -y install \
|
||||
build-essential \
|
||||
cmake \
|
||||
gcc \
|
||||
libcairo2-dev \
|
||||
libgirepository1.0-dev \
|
||||
pkg-config
|
||||
|
||||
# python native
|
||||
RUN apt-get -y install \
|
||||
python3 \
|
||||
python3-dev \
|
||||
python3-pip \
|
||||
python3-setuptools \
|
||||
python3-wheel
|
||||
|
||||
# python pip
|
||||
RUN python3 -m pip install --upgrade pip
|
||||
RUN python3 -m pip install aiofiles debugpy typing_extensions psutil
|
||||
|
||||
ENV SCRYPTED_DOCKER_SERVE="true"
|
||||
ENV SCRYPTED_CAN_RESTART="true"
|
||||
ENV SCRYPTED_VOLUME="/server/volume"
|
||||
ENV SCRYPTED_INSTALL_PATH="/server"
|
||||
|
||||
# changing this forces pip and npm to perform reinstalls.
|
||||
# if this base image changes, this version must be updated.
|
||||
ENV SCRYPTED_BASE_VERSION=20230329
|
||||
ENV SCRYPTED_DOCKER_FLAVOR=lite
|
||||
@@ -1,25 +0,0 @@
|
||||
ARG BUILDPACK_DEPS_BASE="bullseye"
|
||||
FROM debian:${BUILDPACK_DEPS_BASE} as header
|
||||
|
||||
RUN apt-get update && apt-get -y install curl wget
|
||||
|
||||
# switch to nvm?
|
||||
ARG NODE_VERSION=18
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash -
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y nodejs
|
||||
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install software-properties-common apt-utils
|
||||
RUN apt-get -y update
|
||||
|
||||
ENV SCRYPTED_DOCKER_SERVE="true"
|
||||
ENV SCRYPTED_CAN_RESTART="true"
|
||||
ENV SCRYPTED_VOLUME="/server/volume"
|
||||
ENV SCRYPTED_INSTALL_PATH="/server"
|
||||
|
||||
# changing this forces pip and npm to perform reinstalls.
|
||||
# if this base image changes, this version must be updated.
|
||||
ENV SCRYPTED_BASE_VERSION=20230329
|
||||
ENV SCRYPTED_DOCKER_FLAVOR=thin
|
||||
@@ -1,3 +0,0 @@
|
||||
./docker-build.sh
|
||||
|
||||
docker build -t koush/scrypted:18-bullseye-full.nvidia -f Dockerfile.nvidia
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -z "$SCRYPTED_DOCKER_AVAHI" ]
|
||||
then
|
||||
while true
|
||||
do
|
||||
sleep 1000
|
||||
done
|
||||
fi
|
||||
|
||||
until [ -e /var/run/dbus/system_bus_socket ]; do
|
||||
echo "Waiting for dbus..."
|
||||
sleep 1s
|
||||
done
|
||||
echo "Starting Avahi daemon..."
|
||||
exec avahi-daemon --no-chroot -f /etc/avahi/avahi-daemon.conf
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Starting dbus..."
|
||||
exec dbus-daemon --system --nofork
|
||||
@@ -1,18 +0,0 @@
|
||||
################################################################
|
||||
# Begin section generated from template/Dockerfile.full.footer
|
||||
################################################################
|
||||
FROM header as base
|
||||
|
||||
ENV SCRYPTED_DOCKER_SERVE="true"
|
||||
ENV SCRYPTED_CAN_RESTART="true"
|
||||
ENV SCRYPTED_VOLUME="/server/volume"
|
||||
ENV SCRYPTED_INSTALL_PATH="/server"
|
||||
|
||||
# changing this forces pip and npm to perform reinstalls.
|
||||
# if this base image changes, this version must be updated.
|
||||
ENV SCRYPTED_BASE_VERSION=20230329
|
||||
ENV SCRYPTED_DOCKER_FLAVOR=full
|
||||
|
||||
################################################################
|
||||
# End section generated from template/Dockerfile.full.footer
|
||||
################################################################
|
||||
2
external/ring-client-api
vendored
2
external/ring-client-api
vendored
Submodule external/ring-client-api updated: 81f6570f59...4e95093f76
2
external/unifi-protect
vendored
2
external/unifi-protect
vendored
Submodule external/unifi-protect updated: 1f40c63e7f...3759ba334f
2
external/werift
vendored
2
external/werift
vendored
Submodule external/werift updated: 140faa891d...b63f339b55
49
install/config.yaml
Executable file
49
install/config.yaml
Executable file
@@ -0,0 +1,49 @@
|
||||
# Home Assistant Addon Configuration
|
||||
name: Scrypted
|
||||
version: "18-jammy-full.s6-v0.55.0"
|
||||
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
|
||||
panel_icon: mdi:memory
|
||||
hassio_api: true
|
||||
homeassistant_api: true
|
||||
ingress_stream: true
|
||||
host_network: true
|
||||
gpio: true
|
||||
usb: true
|
||||
uart: true
|
||||
video: true
|
||||
image: "ghcr.io/koush/scrypted"
|
||||
environment:
|
||||
SCRYPTED_INSTALL_PLUGIN: "@scrypted/homeassistant"
|
||||
SCRYPTED_VOLUME: "/data/scrypted_data"
|
||||
SCRYPTED_NVR_VOLUME: "/data/scrypted_nvr"
|
||||
SCRYPTED_ADMIN_ADDRESS: "172.30.32.2"
|
||||
SCRYPTED_ADMIN_USERNAME: "homeassistant"
|
||||
SCRYPTED_INSTALL_ENVIRONMENT: "ha"
|
||||
backup_exclude:
|
||||
- '/server/**'
|
||||
- '/data/scrypted_nvr/**'
|
||||
- '/data/scrypted_data/plugins/**'
|
||||
map:
|
||||
- config:rw
|
||||
- media:rw
|
||||
- share:rw
|
||||
devices:
|
||||
- /dev/mem
|
||||
- /dev/dri/renderD128
|
||||
- /dev/apex_0
|
||||
- /dev/apex_1
|
||||
- /dev/apex_2
|
||||
- /dev/apex_3
|
||||
- /dev/dri/card0
|
||||
- /dev/vchiq
|
||||
- /dev/video10
|
||||
- /dev/video0
|
||||
@@ -1,4 +1,4 @@
|
||||
ARG BASE="18-bullseye-full"
|
||||
ARG BASE="18-jammy-full"
|
||||
FROM koush/scrypted-common:${BASE}
|
||||
|
||||
WORKDIR /
|
||||
@@ -1,4 +1,4 @@
|
||||
ARG BASE="16-bullseye"
|
||||
ARG BASE="16-jammy"
|
||||
FROM koush/scrypted-common:${BASE}
|
||||
|
||||
WORKDIR /
|
||||
@@ -6,56 +6,53 @@
|
||||
# This common file will be used by both Docker and the linux
|
||||
# install script.
|
||||
################################################################
|
||||
ARG BUILDPACK_DEPS_BASE="bullseye"
|
||||
FROM debian:${BUILDPACK_DEPS_BASE} as header
|
||||
ARG BASE="jammy"
|
||||
FROM ubuntu:${BASE} as header
|
||||
|
||||
RUN apt-get update && apt-get -y install curl wget
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# switch to nvm?
|
||||
ARG NODE_VERSION=18
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash -
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y nodejs
|
||||
|
||||
# Coral Edge TPU
|
||||
# https://coral.ai/docs/accelerator/get-started/#runtime-on-linux
|
||||
RUN echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" | tee /etc/apt/sources.list.d/coral-edgetpu.list
|
||||
RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y install libedgetpu1-std
|
||||
|
||||
RUN apt-get -y install software-properties-common apt-utils
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
|
||||
# base development stuff
|
||||
RUN apt-get -y install \
|
||||
# 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 \
|
||||
libvips \
|
||||
pkg-config
|
||||
pkg-config && \
|
||||
apt-get -y update && \
|
||||
apt-get -y upgrade
|
||||
|
||||
ARG NODE_VERSION=18
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash -
|
||||
RUN apt-get update && apt-get install -y nodejs
|
||||
|
||||
# python native
|
||||
RUN apt-get -y install \
|
||||
python3 \
|
||||
python3-dev \
|
||||
python3-pip \
|
||||
python3-setuptools \
|
||||
python3-wheel
|
||||
|
||||
# these are necessary for pillow-simd, additional on disk size is small
|
||||
# but could consider removing this.
|
||||
RUN apt-get -y install \
|
||||
libjpeg-dev zlib1g-dev
|
||||
|
||||
# plugins support fallback to pillow, but vips is faster.
|
||||
RUN apt-get -y install \
|
||||
libvips
|
||||
|
||||
# gstreamer native https://gstreamer.freedesktop.org/documentation/installing/on-linux.html?gi-language=c#install-gstreamer-on-ubuntu-or-debian
|
||||
RUN apt-get -y install \
|
||||
gstreamer1.0-tools gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav gstreamer1.0-alsa \
|
||||
gstreamer1.0-vaapi
|
||||
|
||||
# python native
|
||||
# python3 gstreamer bindings
|
||||
RUN apt-get -y install \
|
||||
python3 \
|
||||
python3-dev \
|
||||
python3-gst-1.0 \
|
||||
python3-pip \
|
||||
python3-setuptools \
|
||||
python3-wheel
|
||||
python3-gst-1.0
|
||||
|
||||
# armv7l does not have wheels for any of these
|
||||
# and compile times would forever, if it works at all.
|
||||
@@ -63,22 +60,23 @@ RUN apt-get -y install \
|
||||
# which causes weird behavior in python which looks at the arch version
|
||||
# which still reports 64bit, even if running in 32bit docker.
|
||||
# this scenario is not supported and will be reported at runtime.
|
||||
RUN if [ "$(uname -m)" != "x86_64" ]; \
|
||||
then \
|
||||
apt-get -y install \
|
||||
python3-matplotlib \
|
||||
python3-numpy \
|
||||
python3-opencv \
|
||||
python3-pil \
|
||||
python3-skimage; \
|
||||
fi
|
||||
# this bit is not necessary on amd64, but leaving it for consistency.
|
||||
RUN apt-get -y install \
|
||||
python3-matplotlib \
|
||||
python3-numpy \
|
||||
python3-opencv \
|
||||
python3-pil \
|
||||
python3-skimage
|
||||
|
||||
# allow pip to install to system
|
||||
RUN rm -f /usr/lib/python**/EXTERNALLY-MANAGED
|
||||
|
||||
# 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 --upgrade pip
|
||||
RUN python3 -m pip install --force-reinstall --no-binary :all: cffi
|
||||
RUN python3 -m pip install aiofiles debugpy typing_extensions psutil
|
||||
RUN python3 -m pip install debugpy typing_extensions psutil
|
||||
|
||||
################################################################
|
||||
# End section generated from template/Dockerfile.full.header
|
||||
@@ -88,15 +86,51 @@ RUN python3 -m pip install aiofiles debugpy typing_extensions psutil
|
||||
################################################################
|
||||
FROM header as base
|
||||
|
||||
ENV SCRYPTED_DOCKER_SERVE="true"
|
||||
# intel opencl gpu for openvino
|
||||
RUN bash -c "if [ \"$(uname -m)\" == \"x86_64\" ]; \
|
||||
then \
|
||||
apt-get update && apt-get install -y gpg-agent && \
|
||||
rm -f /usr/share/keyrings/intel-graphics.gpg && \
|
||||
curl -L https://repositories.intel.com/graphics/intel-graphics.key | gpg --dearmor --output /usr/share/keyrings/intel-graphics.gpg && \
|
||||
echo 'deb [arch=amd64,i386 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/graphics/ubuntu jammy arc' | tee /etc/apt/sources.list.d/intel.gpu.jammy.list && \
|
||||
apt-get -y update && \
|
||||
apt-get -y install intel-opencl-icd intel-media-va-driver-non-free && \
|
||||
apt-get -y dist-upgrade; \
|
||||
fi"
|
||||
|
||||
# python 3.9 from ppa.
|
||||
# 3.9 is the version with prebuilt support for tensorflow lite
|
||||
RUN add-apt-repository ppa:deadsnakes/ppa && \
|
||||
apt-get -y install \
|
||||
python3.9 \
|
||||
python3.9-dev \
|
||||
python3.9-distutils
|
||||
|
||||
# allow pip to install to system
|
||||
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
|
||||
# https://coral.ai/docs/accelerator/get-started/#runtime-on-linux
|
||||
RUN echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" | tee /etc/apt/sources.list.d/coral-edgetpu.list
|
||||
RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
|
||||
RUN apt-get -y update && apt-get -y install libedgetpu1-std
|
||||
|
||||
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=20230329
|
||||
ENV SCRYPTED_DOCKER_FLAVOR=full
|
||||
ENV SCRYPTED_BASE_VERSION="20230727"
|
||||
ENV SCRYPTED_DOCKER_FLAVOR="full"
|
||||
|
||||
################################################################
|
||||
# End section generated from template/Dockerfile.full.footer
|
||||
47
install/docker/Dockerfile.lite
Normal file
47
install/docker/Dockerfile.lite
Normal file
@@ -0,0 +1,47 @@
|
||||
ARG BASE="jammy"
|
||||
FROM ubuntu:${BASE} as header
|
||||
|
||||
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 && \
|
||||
apt-get -y update && \
|
||||
apt-get -y upgrade
|
||||
|
||||
ARG NODE_VERSION=18
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash -
|
||||
RUN apt-get 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
|
||||
|
||||
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="lite"
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM koush/18-bullseye-full.s6
|
||||
FROM koush/18-jammy-full.s6
|
||||
|
||||
WORKDIR /
|
||||
|
||||
@@ -19,4 +19,4 @@ 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 aiofiles debugpy typing_extensions psutil
|
||||
RUN python3 -m pip install debugpy typing_extensions psutil
|
||||
@@ -1,23 +1,25 @@
|
||||
ARG BASE="18-bullseye-full"
|
||||
ARG BASE="18-jammy-full"
|
||||
FROM koush/scrypted-common:${BASE}
|
||||
|
||||
# avahi advertiser support
|
||||
RUN apt-get -y install \
|
||||
RUN apt-get update && apt-get -y install \
|
||||
libnss-mdns \
|
||||
avahi-discover \
|
||||
libavahi-compat-libdnssd-dev
|
||||
libavahi-compat-libdnssd-dev \
|
||||
xz-utils
|
||||
|
||||
# copy configurations and scripts
|
||||
COPY fs /
|
||||
|
||||
# s6 process supervisor
|
||||
ARG S6_OVERLAY_VERSION=3.1.1.2
|
||||
ARG S6_OVERLAY_VERSION=3.1.5.0
|
||||
ENV S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0
|
||||
ENV S6_KEEP_ENV=1
|
||||
RUN case "$(uname -m)" in \
|
||||
x86_64) S6_ARCH='x86_64';; \
|
||||
armv7l) S6_ARCH='armhf';; \
|
||||
aarch64) S6_ARCH='aarch64';; \
|
||||
ARG TARGETARCH
|
||||
RUN case "${TARGETARCH}" in \
|
||||
amd64) S6_ARCH='x86_64';; \
|
||||
arm) S6_ARCH='armhf';; \
|
||||
arm64) S6_ARCH='aarch64';; \
|
||||
*) echo "Your system architecture isn't supported."; exit 1 ;; \
|
||||
esac \
|
||||
&& cd /tmp \
|
||||
25
install/docker/Dockerfile.thin
Normal file
25
install/docker/Dockerfile.thin
Normal file
@@ -0,0 +1,25 @@
|
||||
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 curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - && apt-get update && apt-get install -y nodejs
|
||||
|
||||
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"
|
||||
3
install/docker/docker-build-nvidia.sh
Executable file
3
install/docker/docker-build-nvidia.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
./docker-build.sh
|
||||
|
||||
docker build -t koush/scrypted:18-jammy-full.nvidia -f Dockerfile.nvidia
|
||||
@@ -3,15 +3,16 @@
|
||||
set -x
|
||||
|
||||
NODE_VERSION=18
|
||||
BUILDPACK_DEPS_BASE=bullseye
|
||||
SCRYPTED_INSTALL_VERSION=beta
|
||||
IMAGE_BASE=jammy
|
||||
FLAVOR=full
|
||||
BASE=$NODE_VERSION-$BUILDPACK_DEPS_BASE-$FLAVOR
|
||||
BASE=$NODE_VERSION-$IMAGE_BASE-$FLAVOR
|
||||
echo $BASE
|
||||
SUPERVISOR=.s6
|
||||
SUPERVISOR_BASE=$BASE$SUPERVISOR
|
||||
|
||||
docker build -t koush/scrypted-common:$BASE -f Dockerfile.$FLAVOR \
|
||||
--build-arg NODE_VERSION=$NODE_VERSION --build-arg BUILDPACK_DEPS_BASE=$BUILDPACK_DEPS_BASE . && \
|
||||
--build-arg NODE_VERSION=$NODE_VERSION --build-arg BASE=$IMAGE_BASE . && \
|
||||
\
|
||||
docker build -t koush/scrypted:$SUPERVISOR_BASE -f Dockerfile$SUPERVISOR \
|
||||
--build-arg BASE=$BASE .
|
||||
--build-arg BASE=$BASE --build-arg SCRYPTED_INSTALL_VERSION=$SCRYPTED_INSTALL_VERSION .
|
||||
@@ -3,9 +3,10 @@ version: "3.5"
|
||||
# The Scrypted docker-compose.yml file typically resides at:
|
||||
# ~/.scrypted/docker-compose.yml
|
||||
|
||||
|
||||
# Scrypted NVR Storage (Optional Network Volume: Part 1 of 3)
|
||||
# Example volumes SMB (CIFS) and NFS.
|
||||
# Uncomment only one.
|
||||
|
||||
# volumes:
|
||||
# nvr:
|
||||
# driver_opts:
|
||||
@@ -20,35 +21,38 @@ version: "3.5"
|
||||
|
||||
services:
|
||||
scrypted:
|
||||
image: koush/scrypted
|
||||
environment:
|
||||
# Scrypted NVR Storage (Part 2 of 3)
|
||||
|
||||
# Uncomment the next line to configure the NVR plugin to store recordings
|
||||
# use the /nvr directory within the container. This can also be configured
|
||||
# within the plugin manually.
|
||||
# The drive or network share will ALSO need to be configured in the volumes
|
||||
# section below.
|
||||
# - SCRYPTED_NVR_VOLUME=/nvr
|
||||
|
||||
- SCRYPTED_WEBHOOK_UPDATE_AUTHORIZATION=Bearer SET_THIS_TO_SOME_RANDOM_TEXT
|
||||
- SCRYPTED_WEBHOOK_UPDATE=http://localhost:10444/v1/update
|
||||
# nvidia support
|
||||
|
||||
# Uncomment next 3 lines for Nvidia GPU support.
|
||||
# - NVIDIA_VISIBLE_DEVICES=all
|
||||
# - NVIDIA_DRIVER_CAPABILITIES=all
|
||||
# runtime: nvidia
|
||||
container_name: scrypted
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
|
||||
# uncomment this and a line below as needed.
|
||||
# devices:
|
||||
# zwave usb serial device
|
||||
# - /dev/ttyACM0:/dev/ttyACM0
|
||||
# all usb devices, such as coral tpu
|
||||
# - /dev/bus/usb:/dev/bus/usb
|
||||
# intel hardware accelerated video decoding
|
||||
# - /dev/dri:/dev/dri
|
||||
# Uncomment next line to run avahi-daemon inside the container
|
||||
# Don't use if dbus and avahi run on the host and are bind-mounted
|
||||
# (see below under "volumes")
|
||||
# - SCRYPTED_DOCKER_AVAHI=true
|
||||
# runtime: nvidia
|
||||
|
||||
volumes:
|
||||
- ~/.scrypted/volume:/server/volume
|
||||
# modify and add the additional volume for Scrypted NVR
|
||||
# the following example would mount the /mnt/sda/video path on the host
|
||||
# to the /nvr path inside the docker container.
|
||||
# - /mnt/sda/video:/nvr
|
||||
# Scrypted NVR Storage (Part 3 of 3)
|
||||
|
||||
# or use a network mount from one of the examples above
|
||||
# Modify to add the additional volume for Scrypted NVR.
|
||||
# The following example would mount the /mnt/sda/video path on the host
|
||||
# to the /nvr path inside the docker container.
|
||||
# - /mnt/media/video:/nvr
|
||||
|
||||
# Or use a network mount from one of the CIFS/NFS examples at the top of this file.
|
||||
# - type: volume
|
||||
# source: nvr
|
||||
# target: /nvr
|
||||
@@ -57,8 +61,37 @@ services:
|
||||
|
||||
# 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
|
||||
# - /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
|
||||
devices: [
|
||||
# uncomment the common systems devices to pass
|
||||
# them through to docker.
|
||||
|
||||
# all usb devices, such as coral tpu
|
||||
# "/dev/bus/usb:/dev/bus/usb",
|
||||
|
||||
# hardware accelerated video decoding, opencl, etc.
|
||||
# "/dev/dri:/dev/dri",
|
||||
|
||||
# uncomment below as necessary.
|
||||
# zwave usb serial device
|
||||
|
||||
# "/dev/ttyACM0:/dev/ttyACM0",
|
||||
|
||||
# coral PCI devices
|
||||
# "/dev/apex_0:/dev/apex_0",
|
||||
# "/dev/apex_1:/dev/apex_1",
|
||||
]
|
||||
|
||||
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.
|
||||
logging:
|
||||
16
install/docker/fs/etc/s6-overlay/s6-rc.d/avahi/run
Executable file
16
install/docker/fs/etc/s6-overlay/s6-rc.d/avahi/run
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ "${SCRYPTED_DOCKER_AVAHI}" != "true" ]]; then
|
||||
echo "SCRYPTED_DOCKER_AVAHI != true, not starting avahi-daemon" >/dev/stderr
|
||||
while true
|
||||
do
|
||||
sleep 1000
|
||||
done
|
||||
fi
|
||||
|
||||
until [ -e /var/run/dbus/system_bus_socket ]; do
|
||||
echo "Waiting for dbus..."
|
||||
sleep 1s
|
||||
done
|
||||
echo "Starting Avahi daemon..."
|
||||
exec avahi-daemon --no-chroot -f /etc/avahi/avahi-daemon.conf
|
||||
12
install/docker/fs/etc/s6-overlay/s6-rc.d/dbus/run
Executable file
12
install/docker/fs/etc/s6-overlay/s6-rc.d/dbus/run
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ "${SCRYPTED_DOCKER_AVAHI}" != "true" ]]; then
|
||||
echo "SCRYPTED_DOCKER_AVAHI != true, not starting dbus-daemon" >/dev/stderr
|
||||
while true
|
||||
do
|
||||
sleep 1000
|
||||
done
|
||||
fi
|
||||
|
||||
echo "Starting dbus..."
|
||||
exec dbus-daemon --system --nofork
|
||||
@@ -1,5 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ "${SCRYPTED_DOCKER_AVAHI}" != "true" ]]; then
|
||||
echo "SCRYPTED_DOCKER_AVAHI != true, won't manage dbus nor avahi-daemon" >/dev/stderr
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if grep -qE " ((/var)?/run/dbus|(/var)?/run/avahi-daemon(/socket)?) " /proc/mounts; then
|
||||
echo "dbus and/or avahi-daemon volumes are bind mounted, won't touch them" >/dev/stderr
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# make run folders
|
||||
mkdir -p /var/run/dbus
|
||||
mkdir -p /var/run/avahi-daemon
|
||||
@@ -22,4 +32,4 @@ if [ ! -z "$DSM_HOSTNAME" ]; then
|
||||
sed -i "s/.*host-name.*/host-name=${DSM_HOSTNAME}/" /etc/avahi/avahi-daemon.conf
|
||||
else
|
||||
sed -i "s/.*host-name.*/#host-name=/" /etc/avahi/avahi-daemon.conf
|
||||
fi
|
||||
fi
|
||||
@@ -42,7 +42,11 @@ fi
|
||||
WATCHTOWER_HTTP_API_TOKEN=$(echo $RANDOM | md5sum)
|
||||
DOCKER_COMPOSE_YML=$SCRYPTED_HOME/docker-compose.yml
|
||||
echo "Created $DOCKER_COMPOSE_YML"
|
||||
curl -s https://raw.githubusercontent.com/koush/scrypted/main/docker/docker-compose.yml | sed s/SET_THIS_TO_SOME_RANDOM_TEXT/"$(echo $RANDOM | md5sum | head -c 32)"/g > $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
|
||||
fi
|
||||
|
||||
echo "Setting permissions on $SCRYPTED_HOME"
|
||||
chown -R $SERVICE_USER $SCRYPTED_HOME
|
||||
54
install/docker/template/Dockerfile.full.footer
Normal file
54
install/docker/template/Dockerfile.full.footer
Normal file
@@ -0,0 +1,54 @@
|
||||
################################################################
|
||||
# Begin section generated from template/Dockerfile.full.footer
|
||||
################################################################
|
||||
FROM header as base
|
||||
|
||||
# intel opencl gpu for openvino
|
||||
RUN bash -c "if [ \"$(uname -m)\" == \"x86_64\" ]; \
|
||||
then \
|
||||
apt-get update && apt-get install -y gpg-agent && \
|
||||
rm -f /usr/share/keyrings/intel-graphics.gpg && \
|
||||
curl -L https://repositories.intel.com/graphics/intel-graphics.key | gpg --dearmor --output /usr/share/keyrings/intel-graphics.gpg && \
|
||||
echo 'deb [arch=amd64,i386 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/graphics/ubuntu jammy arc' | tee /etc/apt/sources.list.d/intel.gpu.jammy.list && \
|
||||
apt-get -y update && \
|
||||
apt-get -y install intel-opencl-icd intel-media-va-driver-non-free && \
|
||||
apt-get -y dist-upgrade; \
|
||||
fi"
|
||||
|
||||
# python 3.9 from ppa.
|
||||
# 3.9 is the version with prebuilt support for tensorflow lite
|
||||
RUN add-apt-repository ppa:deadsnakes/ppa && \
|
||||
apt-get -y install \
|
||||
python3.9 \
|
||||
python3.9-dev \
|
||||
python3.9-distutils
|
||||
|
||||
# allow pip to install to system
|
||||
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
|
||||
# https://coral.ai/docs/accelerator/get-started/#runtime-on-linux
|
||||
RUN echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" | tee /etc/apt/sources.list.d/coral-edgetpu.list
|
||||
RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
|
||||
RUN apt-get -y update && apt-get -y install libedgetpu1-std
|
||||
|
||||
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="full"
|
||||
|
||||
################################################################
|
||||
# End section generated from template/Dockerfile.full.footer
|
||||
################################################################
|
||||
@@ -3,56 +3,53 @@
|
||||
# This common file will be used by both Docker and the linux
|
||||
# install script.
|
||||
################################################################
|
||||
ARG BUILDPACK_DEPS_BASE="bullseye"
|
||||
FROM debian:${BUILDPACK_DEPS_BASE} as header
|
||||
ARG BASE="jammy"
|
||||
FROM ubuntu:${BASE} as header
|
||||
|
||||
RUN apt-get update && apt-get -y install curl wget
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# switch to nvm?
|
||||
ARG NODE_VERSION=18
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash -
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y nodejs
|
||||
|
||||
# Coral Edge TPU
|
||||
# https://coral.ai/docs/accelerator/get-started/#runtime-on-linux
|
||||
RUN echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" | tee /etc/apt/sources.list.d/coral-edgetpu.list
|
||||
RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y install libedgetpu1-std
|
||||
|
||||
RUN apt-get -y install software-properties-common apt-utils
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
|
||||
# base development stuff
|
||||
RUN apt-get -y install \
|
||||
# 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 \
|
||||
libvips \
|
||||
pkg-config
|
||||
pkg-config && \
|
||||
apt-get -y update && \
|
||||
apt-get -y upgrade
|
||||
|
||||
ARG NODE_VERSION=18
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash -
|
||||
RUN apt-get update && apt-get install -y nodejs
|
||||
|
||||
# python native
|
||||
RUN apt-get -y install \
|
||||
python3 \
|
||||
python3-dev \
|
||||
python3-pip \
|
||||
python3-setuptools \
|
||||
python3-wheel
|
||||
|
||||
# these are necessary for pillow-simd, additional on disk size is small
|
||||
# but could consider removing this.
|
||||
RUN apt-get -y install \
|
||||
libjpeg-dev zlib1g-dev
|
||||
|
||||
# plugins support fallback to pillow, but vips is faster.
|
||||
RUN apt-get -y install \
|
||||
libvips
|
||||
|
||||
# gstreamer native https://gstreamer.freedesktop.org/documentation/installing/on-linux.html?gi-language=c#install-gstreamer-on-ubuntu-or-debian
|
||||
RUN apt-get -y install \
|
||||
gstreamer1.0-tools gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav gstreamer1.0-alsa \
|
||||
gstreamer1.0-vaapi
|
||||
|
||||
# python native
|
||||
# python3 gstreamer bindings
|
||||
RUN apt-get -y install \
|
||||
python3 \
|
||||
python3-dev \
|
||||
python3-gst-1.0 \
|
||||
python3-pip \
|
||||
python3-setuptools \
|
||||
python3-wheel
|
||||
python3-gst-1.0
|
||||
|
||||
# armv7l does not have wheels for any of these
|
||||
# and compile times would forever, if it works at all.
|
||||
@@ -60,22 +57,23 @@ RUN apt-get -y install \
|
||||
# which causes weird behavior in python which looks at the arch version
|
||||
# which still reports 64bit, even if running in 32bit docker.
|
||||
# this scenario is not supported and will be reported at runtime.
|
||||
RUN if [ "$(uname -m)" != "x86_64" ]; \
|
||||
then \
|
||||
apt-get -y install \
|
||||
python3-matplotlib \
|
||||
python3-numpy \
|
||||
python3-opencv \
|
||||
python3-pil \
|
||||
python3-skimage; \
|
||||
fi
|
||||
# this bit is not necessary on amd64, but leaving it for consistency.
|
||||
RUN apt-get -y install \
|
||||
python3-matplotlib \
|
||||
python3-numpy \
|
||||
python3-opencv \
|
||||
python3-pil \
|
||||
python3-skimage
|
||||
|
||||
# allow pip to install to system
|
||||
RUN rm -f /usr/lib/python**/EXTERNALLY-MANAGED
|
||||
|
||||
# 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 --upgrade pip
|
||||
RUN python3 -m pip install --force-reinstall --no-binary :all: cffi
|
||||
RUN python3 -m pip install aiofiles debugpy typing_extensions psutil
|
||||
RUN python3 -m pip install debugpy typing_extensions psutil
|
||||
|
||||
################################################################
|
||||
# End section generated from template/Dockerfile.full.header
|
||||
BIN
install/icon.png
Normal file
BIN
install/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
@@ -45,10 +45,10 @@ ARG() {
|
||||
}
|
||||
|
||||
ENV() {
|
||||
echo "ignoring ENV $1"
|
||||
export $@
|
||||
}
|
||||
|
||||
source <(curl -s https://raw.githubusercontent.com/koush/scrypted/main/docker/template/Dockerfile.full.header)
|
||||
source <(curl -s https://raw.githubusercontent.com/koush/scrypted/main/install/docker/template/Dockerfile.full.header)
|
||||
|
||||
if [ -z "$SERVICE_USER" ]
|
||||
then
|
||||
@@ -58,6 +58,9 @@ brew unpin gst-python
|
||||
|
||||
### END HACK WORKAROUND
|
||||
|
||||
# seems to be necessary for python-codecs' pycairo dependency or something?
|
||||
RUN_IGNORE gobject-introspection libffi pkg-config
|
||||
|
||||
# gstreamer plugins
|
||||
RUN_IGNORE brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-libav
|
||||
# gst python bindings
|
||||
@@ -87,7 +90,7 @@ if [ "$PYTHON_VERSION" != "3.10" ]
|
||||
then
|
||||
RUN python$PYTHON_VERSION -m pip install typing
|
||||
fi
|
||||
RUN python$PYTHON_VERSION -m pip install aiofiles debugpy typing_extensions opencv-python psutil
|
||||
RUN python$PYTHON_VERSION -m pip install debugpy typing_extensions opencv-python psutil
|
||||
|
||||
echo "Installing Scrypted Launch Agent..."
|
||||
|
||||
@@ -20,7 +20,7 @@ $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 aiofiles debugpy typing_extensions typing opencv-python
|
||||
py $SCRYPTED_WINDOWS_PYTHON_VERSION -m pip install debugpy typing_extensions typing opencv-python
|
||||
|
||||
npx -y scrypted@latest install-server
|
||||
|
||||
BIN
install/logo.png
Normal file
BIN
install/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
8
packages/cli/.vscode/launch.json
vendored
8
packages/cli/.vscode/launch.json
vendored
@@ -19,11 +19,11 @@
|
||||
"-r",
|
||||
"ts-node/register"
|
||||
],
|
||||
"preLaunchTask": "npm: build",
|
||||
"args": [
|
||||
"ffplay",
|
||||
"Kitchen",
|
||||
"getRecordingStream",
|
||||
"{\"startTime\":1677699495709}"
|
||||
"Baby Camera@192.168.2.109",
|
||||
"getVideoStream",
|
||||
],
|
||||
"sourceMaps": true,
|
||||
"resolveSourceMapLocations": [
|
||||
@@ -35,4 +35,4 @@
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
4
packages/cli/package-lock.json
generated
4
packages/cli/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "scrypted",
|
||||
"version": "1.0.67",
|
||||
"version": "1.0.69",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "scrypted",
|
||||
"version": "1.0.67",
|
||||
"version": "1.0.69",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/client": "^1.1.43",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "scrypted",
|
||||
"version": "1.0.67",
|
||||
"version": "1.0.69",
|
||||
"description": "",
|
||||
"main": "./dist/main.js",
|
||||
"bin": {
|
||||
|
||||
@@ -172,8 +172,11 @@ async function main() {
|
||||
ffmpegInput.inputArguments = ffmpegInput.inputArguments.map(i => i === ffmpegInput.url ? ffmpegInput.urls?.[0] : i);
|
||||
}
|
||||
}
|
||||
console.log('ffplay', ...ffmpegInput.inputArguments);
|
||||
child_process.spawn('ffplay', ffmpegInput.inputArguments, {
|
||||
const args = [...ffmpegInput.inputArguments];
|
||||
if (ffmpegInput.h264FilterArguments)
|
||||
args.push(...ffmpegInput.h264FilterArguments);
|
||||
console.log('ffplay', ...args);
|
||||
child_process.spawn('ffplay', args, {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
sdk.disconnect();
|
||||
|
||||
@@ -23,10 +23,19 @@ async function example() {
|
||||
if (!backyard)
|
||||
throw new Error('Device not found');
|
||||
|
||||
backyard.listen(ScryptedInterface.ObjectDetector, (source, details, data) => {
|
||||
backyard.listen(ScryptedInterface.ObjectDetector, async (source, details, data) => {
|
||||
const results = data as ObjectsDetected;
|
||||
console.log(results);
|
||||
})
|
||||
console.log('detection results', results);
|
||||
// detections that are flagged for retention will have a detectionId.
|
||||
// tf etc won't retain automatically, and this requires a wrapping detector like Scrypted NVR Object Detection
|
||||
// to decide which frames to keep. Otherwise saving all images would be extremely poor performance.
|
||||
if (!results.detectionId)
|
||||
return;
|
||||
|
||||
const media = await backyard.getDetectionInput(results.detectionId);
|
||||
const jpeg = await sdk.mediaManager.convertMediaObjectToBuffer(media, 'image/jpeg');
|
||||
// do something with the buffer like save to disk or send to a service.
|
||||
});
|
||||
}
|
||||
|
||||
example();
|
||||
|
||||
553
packages/client/package-lock.json
generated
553
packages/client/package-lock.json
generated
@@ -1,29 +1,54 @@
|
||||
{
|
||||
"name": "@scrypted/client",
|
||||
"version": "1.1.51",
|
||||
"version": "1.1.57",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/client",
|
||||
"version": "1.1.51",
|
||||
"version": "1.1.57",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/types": "^0.2.80",
|
||||
"@scrypted/types": "^0.2.95",
|
||||
"axios": "^0.25.0",
|
||||
"engine.io-client": "^6.4.0",
|
||||
"rimraf": "^3.0.2"
|
||||
"engine.io-client": "^6.5.2",
|
||||
"rimraf": "^5.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ip": "^1.1.0",
|
||||
"@types/node": "^18.14.2",
|
||||
"typescript": "^4.9.5"
|
||||
"@types/ip": "^1.1.1",
|
||||
"@types/node": "^20.8.4",
|
||||
"typescript": "^5.2.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==",
|
||||
"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==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/types": {
|
||||
"version": "0.2.80",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.2.80.tgz",
|
||||
"integrity": "sha512-YVu7jcD5sYgjJLP7kH1K2FJzqrlcjdpDxzZoLXudZCKiujldbmLYcwglSgnN9bRqkKZcGOfru/WssvQj+0JioQ=="
|
||||
"version": "0.2.95",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.2.95.tgz",
|
||||
"integrity": "sha512-gdSCsvGp1ZZowLOKP4CaxdTavnrE/bBfcfnvwsrPcxVRjbh+85fiNnXH2nX6L9uikAAPY3cIlcwbw3Dv1wzGQA=="
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.0",
|
||||
@@ -31,19 +56,44 @@
|
||||
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
|
||||
},
|
||||
"node_modules/@types/ip": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/ip/-/ip-1.1.0.tgz",
|
||||
"integrity": "sha512-dwNe8gOoF70VdL6WJBwVHtQmAX4RMd62M+mAB9HQFjG1/qiCLM/meRy95Pd14FYBbEDwCq7jgJs89cHpLBu4HQ==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ip/-/ip-1.1.1.tgz",
|
||||
"integrity": "sha512-/v+XZuKNBQHJi3dKeFt9LySLzWNkgmaYRtnFfg27Ag0MO9tQLzHUuAA8zOhPtbDvDGkcnZGr4pVZQPGNft/WYA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.2.tgz",
|
||||
"integrity": "sha512-1uEQxww3DaghA0RxqHx0O0ppVlo43pJhepY51OxuQIKHpjbnYLA7vcdwioNPzIqmC2u3I/dmylcqjlh0e7AyUA==",
|
||||
"dev": true
|
||||
"version": "20.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.4.tgz",
|
||||
"integrity": "sha512-ZVPnqU58giiCjSxjVUESDtdPk4QR5WQhhINbc9UBrKLU68MX5BF6kbQzTrkwbolyr0X8ChBpXfavr5mZFKZQ5A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.25.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
||||
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.25.0",
|
||||
@@ -59,18 +109,41 @@
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
"which": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
@@ -88,22 +161,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
|
||||
},
|
||||
"node_modules/engine.io-client": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.4.0.tgz",
|
||||
"integrity": "sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==",
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.2.tgz",
|
||||
"integrity": "sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.0.3",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.11.0",
|
||||
"xmlhttprequest-ssl": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz",
|
||||
"integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==",
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz",
|
||||
"integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
@@ -127,53 +210,100 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
|
||||
"integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
"cross-spawn": "^7.0.0",
|
||||
"signal-exit": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"node_modules/glob": {
|
||||
"version": "10.3.10",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
|
||||
"integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
|
||||
"dependencies": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"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/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
"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": "*"
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
||||
},
|
||||
"node_modules/jackspeak": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
|
||||
"integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
|
||||
"dependencies": {
|
||||
"@isaacs/cliui": "^8.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz",
|
||||
"integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==",
|
||||
"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==",
|
||||
"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==",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
@@ -181,53 +311,280 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"node_modules/path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"node_modules/path-scurry": {
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
|
||||
"integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
"lru-cache": "^9.1.1 || ^10.0.0",
|
||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
"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==",
|
||||
"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==",
|
||||
"dependencies": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/signal-exit": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||
"dependencies": {
|
||||
"eastasianwidth": "^0.2.0",
|
||||
"emoji-regex": "^9.2.2",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs": {
|
||||
"name": "string-width",
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi-cjs": {
|
||||
"name": "strip-ansi",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
||||
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.25.3",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz",
|
||||
"integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==",
|
||||
"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==",
|
||||
"dependencies": {
|
||||
"isexe": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"node-which": "bin/node-which"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.1.0",
|
||||
"string-width": "^5.0.1",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs": {
|
||||
"name": "wrap-ansi",
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.11.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/client",
|
||||
"version": "1.1.51",
|
||||
"version": "1.1.57",
|
||||
"description": "",
|
||||
"main": "dist/packages/client/src/index.js",
|
||||
"scripts": {
|
||||
@@ -12,14 +12,14 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/ip": "^1.1.0",
|
||||
"@types/node": "^18.14.2",
|
||||
"typescript": "^4.9.5"
|
||||
"@types/ip": "^1.1.1",
|
||||
"@types/node": "^20.8.4",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@scrypted/types": "^0.2.80",
|
||||
"@scrypted/types": "^0.2.95",
|
||||
"axios": "^0.25.0",
|
||||
"engine.io-client": "^6.4.0",
|
||||
"rimraf": "^3.0.2"
|
||||
"engine.io-client": "^6.5.2",
|
||||
"rimraf": "^5.0.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MediaObjectOptions, RTCConnectionManagement, RTCSignalingSession, ScryptedStatic } from "@scrypted/types";
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
import axios, { AxiosRequestConfig, AxiosRequestHeaders } from 'axios';
|
||||
import * as eio from 'engine.io-client';
|
||||
import { SocketOptions } from 'engine.io-client';
|
||||
import { Deferred } from "../../../common/src/deferred";
|
||||
@@ -8,7 +8,6 @@ import { BrowserSignalingSession, waitPeerConnectionIceConnected, waitPeerIceCon
|
||||
import { DataChannelDebouncer } from "../../../plugins/webrtc/src/datachannel-debouncer";
|
||||
import type { IOSocket } from '../../../server/src/io';
|
||||
import { MediaObject } from '../../../server/src/plugin/mediaobject';
|
||||
import type { MediaObjectRemote } from '../../../server/src/plugin/plugin-api';
|
||||
import { attachPluginRemote } from '../../../server/src/plugin/plugin-remote';
|
||||
import { RpcPeer } from '../../../server/src/rpc';
|
||||
import { createRpcDuplexSerializer, createRpcSerializer } from '../../../server/src/rpc-serializer';
|
||||
@@ -48,9 +47,8 @@ export interface ScryptedClientStatic extends ScryptedStatic {
|
||||
browserSignalingSession?: BrowserSignalingSession;
|
||||
address?: string;
|
||||
connectionType: ScryptedClientConnectionType;
|
||||
authorization?: string;
|
||||
queryToken?: { [parameter: string]: string };
|
||||
rpcPeer: RpcPeer,
|
||||
rpcPeer: RpcPeer;
|
||||
loginResult: ScryptedClientLoginResult;
|
||||
}
|
||||
|
||||
export interface ScryptedConnectionOptions {
|
||||
@@ -59,6 +57,7 @@ export interface ScryptedConnectionOptions {
|
||||
webrtc?: boolean;
|
||||
baseUrl?: string;
|
||||
axiosConfig?: AxiosRequestConfig;
|
||||
previousLoginResult?: ScryptedClientLoginResult;
|
||||
}
|
||||
|
||||
export interface ScryptedLoginOptions extends ScryptedConnectionOptions {
|
||||
@@ -78,25 +77,48 @@ export interface ScryptedClientOptions extends Partial<ScryptedLoginOptions> {
|
||||
transports?: string[];
|
||||
}
|
||||
|
||||
function isInstalledApp() {
|
||||
return globalThis.navigator?.userAgent.includes('InstalledApp');
|
||||
}
|
||||
|
||||
function isRunningStandalone() {
|
||||
return globalThis.matchMedia?.('(display-mode: standalone)').matches || globalThis.navigator?.userAgent.includes('InstalledApp');
|
||||
return globalThis.matchMedia?.('(display-mode: standalone)').matches || isInstalledApp();
|
||||
}
|
||||
|
||||
export async function logoutScryptedClient(baseUrl?: string) {
|
||||
const url = baseUrl ? new URL('/logout', baseUrl).toString() : '/logout';
|
||||
const url = combineBaseUrl(baseUrl, 'logout');
|
||||
const response = await axios(url, {
|
||||
withCredentials: true,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export function getCurrentBaseUrl() {
|
||||
// an endpoint within scrypted will be served at /endpoint/[org/][id]
|
||||
// find the endpoint prefix and anything prior to that will be the server base url.
|
||||
const url = new URL(window.location.href);
|
||||
url.search = '';
|
||||
url.hash = '';
|
||||
let endpointPath = window.location.pathname;
|
||||
const parts = endpointPath.split('/');
|
||||
const index = parts.findIndex(p => p === 'endpoint');
|
||||
if (index === -1) {
|
||||
// console.warn('path not recognized, does not contain the segment "endpoint".')
|
||||
return undefined;
|
||||
}
|
||||
const keep = parts.slice(0, index);
|
||||
keep.push('');
|
||||
url.pathname = keep.join('/');
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
export async function loginScryptedClient(options: ScryptedLoginOptions) {
|
||||
let { baseUrl, username, password, change_password, maxAge } = options;
|
||||
// pwa should stay logged in for a year.
|
||||
if (!maxAge && isRunningStandalone())
|
||||
maxAge = 365 * 24 * 60 * 60 * 1000;
|
||||
|
||||
const url = `${baseUrl || ''}/login`;
|
||||
const url = combineBaseUrl(baseUrl, 'login');
|
||||
const response = await axios.post(url, {
|
||||
username,
|
||||
password,
|
||||
@@ -110,34 +132,43 @@ export async function loginScryptedClient(options: ScryptedLoginOptions) {
|
||||
if (response.status !== 200)
|
||||
throw new Error('status ' + response.status);
|
||||
|
||||
const addresses = response.data.addresses as string[] || [];
|
||||
// the cloud plugin will include this header.
|
||||
// should maybe move this into the cloud server itself.
|
||||
const scryptedCloud = response.headers['x-scrypted-cloud'] === 'true';
|
||||
const directAddress = response.headers['x-scrypted-direct-address'];
|
||||
|
||||
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,
|
||||
scryptedCloud,
|
||||
directAddress,
|
||||
addresses: response.data.addresses as string[],
|
||||
externalAddresses: response.data.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'],
|
||||
};
|
||||
}
|
||||
|
||||
export async function checkScryptedClientLogin(options?: ScryptedConnectionOptions) {
|
||||
let { baseUrl } = options || {};
|
||||
const url = `${baseUrl || ''}/login`;
|
||||
let url = combineBaseUrl(baseUrl, 'login');
|
||||
const headers: AxiosRequestHeaders = {};
|
||||
if (options?.previousLoginResult?.queryToken) {
|
||||
// headers.Authorization = options?.previousLoginResult?.authorization;
|
||||
// const search = new URLSearchParams(options.previousLoginResult.queryToken);
|
||||
// url += '?' + search.toString();
|
||||
const token = options?.previousLoginResult.username + ":" + options.previousLoginResult.token;
|
||||
const hash = Buffer.from(token).toString('base64');
|
||||
|
||||
headers.Authorization = `Basic ${hash}`;
|
||||
}
|
||||
const response = await axios.get(url, {
|
||||
withCredentials: true,
|
||||
headers,
|
||||
...options?.axiosConfig,
|
||||
});
|
||||
const scryptedCloud = response.headers['x-scrypted-cloud'] === 'true';
|
||||
const directAddress = response.headers['x-scrypted-direct-address'];
|
||||
|
||||
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,
|
||||
@@ -145,12 +176,29 @@ export async function checkScryptedClientLogin(options?: ScryptedConnectionOptio
|
||||
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[],
|
||||
scryptedCloud,
|
||||
directAddress,
|
||||
externalAddresses: response.data.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'],
|
||||
};
|
||||
}
|
||||
|
||||
export interface ScryptedClientLoginResult {
|
||||
username: string;
|
||||
token: string;
|
||||
authorization: string;
|
||||
queryToken: { [parameter: string]: string };
|
||||
localAddresses: string[];
|
||||
externalAddresses: string[];
|
||||
scryptedCloud: boolean;
|
||||
directAddress: string;
|
||||
cloudAddress: string;
|
||||
}
|
||||
|
||||
export class ScryptedClientLoginError extends Error {
|
||||
constructor(public result: Awaited<ReturnType<typeof checkScryptedClientLogin>>) {
|
||||
super(result.error);
|
||||
@@ -175,53 +223,130 @@ export function redirectScryptedLogin(options?: {
|
||||
globalThis.location.href = redirect_uri;
|
||||
}
|
||||
|
||||
export function combineBaseUrl(baseUrl: string, rootPath: string) {
|
||||
return baseUrl ? new URL(rootPath, baseUrl).toString() : '/' + rootPath;
|
||||
}
|
||||
|
||||
export async function redirectScryptedLogout(baseUrl?: string) {
|
||||
baseUrl = baseUrl || '';
|
||||
globalThis.location.href = `${baseUrl}/logout`;
|
||||
globalThis.location.href = combineBaseUrl(baseUrl, 'logout');
|
||||
}
|
||||
|
||||
export async function connectScryptedClient(options: ScryptedClientOptions): Promise<ScryptedClientStatic> {
|
||||
const start = Date.now();
|
||||
let { baseUrl, pluginId, clientName, username, password } = options;
|
||||
|
||||
let authorization: string;
|
||||
let queryToken: any;
|
||||
|
||||
const extraHeaders: { [header: string]: string } = {};
|
||||
let localAddresses: string[];
|
||||
let externalAddresses: string[];
|
||||
let scryptedCloud: boolean;
|
||||
let directAddress: string;
|
||||
let cloudAddress: string;
|
||||
let token: string;
|
||||
|
||||
console.log('@scrypted/client', packageJson.version);
|
||||
|
||||
const extraHeaders: { [header: string]: string } = {};
|
||||
|
||||
// Chrome will complain about websites making xhr requests to self signed https sites, even
|
||||
// if the cert has been accepted. Other browsers seem fine.
|
||||
// So the default is not to connect to IP addresses on Chrome, but do so on other browsers.
|
||||
const isChrome = globalThis.navigator?.userAgent.includes('Chrome');
|
||||
const isNotChromeOrIsInstalledApp = !isChrome || isInstalledApp();
|
||||
let tryAlternateAddresses = false;
|
||||
|
||||
if (username && password) {
|
||||
const loginResult = await loginScryptedClient(options as ScryptedLoginOptions);
|
||||
if (loginResult.authorization)
|
||||
extraHeaders['Authorization'] = loginResult.authorization;
|
||||
localAddresses = loginResult.addresses;
|
||||
externalAddresses = loginResult.externalAddresses;
|
||||
scryptedCloud = loginResult.scryptedCloud;
|
||||
directAddress = loginResult.directAddress;
|
||||
cloudAddress = loginResult.cloudAddress;
|
||||
authorization = loginResult.authorization;
|
||||
queryToken = loginResult.queryToken;
|
||||
token = loginResult.token;
|
||||
console.log('login result', Date.now() - start, loginResult);
|
||||
}
|
||||
else {
|
||||
const loginCheck = await checkScryptedClientLogin({
|
||||
const urlsToCheck = new Set<string>();
|
||||
if (options?.previousLoginResult?.token) {
|
||||
for (const u of [
|
||||
...options?.previousLoginResult?.localAddresses || [],
|
||||
options?.previousLoginResult?.directAddress,
|
||||
]) {
|
||||
if (u && (isNotChromeOrIsInstalledApp || options.direct))
|
||||
urlsToCheck.add(u);
|
||||
}
|
||||
for (const u of [
|
||||
...options?.previousLoginResult?.externalAddresses || [],
|
||||
options?.previousLoginResult?.cloudAddress,
|
||||
]) {
|
||||
if (u)
|
||||
urlsToCheck.add(u);
|
||||
}
|
||||
}
|
||||
|
||||
// the alternate urls must have a valid response.
|
||||
const loginCheckPromises = [...urlsToCheck].map(async baseUrl => {
|
||||
const loginCheck = await checkScryptedClientLogin({
|
||||
baseUrl,
|
||||
previousLoginResult: options?.previousLoginResult,
|
||||
});
|
||||
|
||||
if (loginCheck.error || loginCheck.redirect)
|
||||
throw new Error('login error');
|
||||
|
||||
if (!loginCheck.authorization || !loginCheck.username || !loginCheck.queryToken) {
|
||||
console.error(loginCheck);
|
||||
throw new Error('malformed login result');
|
||||
}
|
||||
|
||||
return loginCheck;
|
||||
});
|
||||
|
||||
const baseUrlCheck = checkScryptedClientLogin({
|
||||
baseUrl,
|
||||
});
|
||||
loginCheckPromises.push(baseUrlCheck);
|
||||
|
||||
let loginCheck: Awaited<ReturnType<typeof checkScryptedClientLogin>>;
|
||||
try {
|
||||
loginCheck = await Promise.any(loginCheckPromises);
|
||||
tryAlternateAddresses ||= loginCheck.baseUrl !== baseUrl;
|
||||
}
|
||||
catch (e) {
|
||||
loginCheck = await baseUrlCheck;
|
||||
}
|
||||
|
||||
if (tryAlternateAddresses)
|
||||
console.log('Found direct login. Allowing alternate addresses.')
|
||||
|
||||
if (loginCheck.error || loginCheck.redirect)
|
||||
throw new ScryptedClientLoginError(loginCheck);
|
||||
localAddresses = loginCheck.addresses;
|
||||
externalAddresses = loginCheck.externalAddresses;
|
||||
scryptedCloud = loginCheck.scryptedCloud;
|
||||
directAddress = loginCheck.directAddress;
|
||||
cloudAddress = loginCheck.cloudAddress;
|
||||
username = loginCheck.username;
|
||||
authorization = loginCheck.authorization;
|
||||
queryToken = loginCheck.queryToken;
|
||||
token = loginCheck.token;
|
||||
console.log('login checked', Date.now() - start, loginCheck);
|
||||
}
|
||||
|
||||
let socket: IOClientSocket;
|
||||
const endpointPath = `/endpoint/${pluginId}`;
|
||||
const eioPath = `endpoint/${pluginId}/engine.io/api`;
|
||||
const eioEndpoint = baseUrl ? new URL(eioPath, baseUrl).pathname : '/' + eioPath;
|
||||
// https://github.com/socketio/engine.io/issues/690
|
||||
const cacehBust = Math.random().toString(36).substring(3, 10);
|
||||
const eioOptions: Partial<SocketOptions> = {
|
||||
path: `${endpointPath}/engine.io/api`,
|
||||
path: eioEndpoint,
|
||||
query: {
|
||||
cacehBust,
|
||||
},
|
||||
withCredentials: true,
|
||||
extraHeaders,
|
||||
rejectUnauthorized: false,
|
||||
@@ -234,24 +359,45 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
// watch for this flush.
|
||||
const flush = new Deferred<void>();
|
||||
|
||||
// Chrome will complain about websites making xhr requests to self signed https sites, even
|
||||
// if the cert has been accepted. Other browsers seem fine.
|
||||
// So the default is not to connect to IP addresses on Chrome, but do so on other browsers.
|
||||
const isChrome = globalThis.navigator?.userAgent.includes('Chrome');
|
||||
|
||||
const addresses: string[] = [];
|
||||
const localAddressDefault = !isChrome;
|
||||
if (((scryptedCloud && options.local === undefined && localAddressDefault) || options.local) && localAddresses) {
|
||||
const localAddressDefault = isNotChromeOrIsInstalledApp;
|
||||
|
||||
tryAlternateAddresses ||= scryptedCloud;
|
||||
|
||||
if (((tryAlternateAddresses && options.local === undefined && localAddressDefault) || options.local) && localAddresses) {
|
||||
addresses.push(...localAddresses);
|
||||
}
|
||||
|
||||
const directAddressDefault = directAddress && (!isChrome || !isIPAddress(directAddress));
|
||||
if (((scryptedCloud && options.direct === undefined && directAddressDefault) || options.direct) && directAddress) {
|
||||
const directAddressDefault = directAddress && (isNotChromeOrIsInstalledApp || !isIPAddress(directAddress));
|
||||
if (((tryAlternateAddresses && options.direct === undefined && directAddressDefault) || options.direct) && directAddress) {
|
||||
addresses.push(directAddress);
|
||||
}
|
||||
|
||||
if ((tryAlternateAddresses && options.direct === undefined) || options.direct) {
|
||||
if (cloudAddress)
|
||||
addresses.push(cloudAddress);
|
||||
for (const externalAddress of externalAddresses || []) {
|
||||
addresses.push(externalAddress);
|
||||
}
|
||||
}
|
||||
|
||||
const tryAddresses = !!addresses.length;
|
||||
const tryWebrtc = !!globalThis.RTCPeerConnection && (scryptedCloud && options.webrtc === undefined) || options.webrtc;
|
||||
const webrtcLastFailedKey = 'webrtcLastFailed';
|
||||
const canUseWebrtc = !!globalThis.RTCPeerConnection;
|
||||
let tryWebrtc = canUseWebrtc && options.webrtc;
|
||||
// try webrtc by default on scrypted cloud.
|
||||
// but webrtc takes a while to fail, so backoff if it fails to prevent continual slow starts.
|
||||
if (scryptedCloud && canUseWebrtc && globalThis.localStorage && options.webrtc === undefined) {
|
||||
tryWebrtc = true;
|
||||
const webrtcLastFailed = parseFloat(localStorage.getItem(webrtcLastFailedKey));
|
||||
// if webrtc has failed in the past day, dont attempt to use it.
|
||||
const now = Date.now();
|
||||
if (webrtcLastFailed < now && webrtcLastFailed > now - 1 * 24 * 60 * 60 * 1000) {
|
||||
tryWebrtc = false;
|
||||
console.warn('WebRTC API connection recently failed. Skipping.')
|
||||
}
|
||||
}
|
||||
|
||||
console.log({
|
||||
tryLocalAddressess: tryAddresses,
|
||||
tryWebrtc,
|
||||
@@ -279,7 +425,7 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
// It is probably better to simply prompt and redirect to the LAN address
|
||||
// if it is reacahble.
|
||||
|
||||
for (const address of addresses) {
|
||||
for (const address of new Set(addresses)) {
|
||||
console.log('trying', address);
|
||||
const check = new eio.Socket(address, localEioOptions);
|
||||
sockets.push(check);
|
||||
@@ -298,6 +444,9 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
console.log('trying webrtc');
|
||||
const webrtcEioOptions: Partial<SocketOptions> = {
|
||||
path: '/endpoint/@scrypted/webrtc/engine.io/',
|
||||
query: {
|
||||
cacehBust,
|
||||
},
|
||||
withCredentials: true,
|
||||
extraHeaders,
|
||||
rejectUnauthorized: false,
|
||||
@@ -428,7 +577,7 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
const p2pPromises = [...promises];
|
||||
|
||||
promises.push((async () => {
|
||||
const waitDuration = tryWebrtc ? 3000 : (tryAddresses ? 1000 : 0);
|
||||
const waitDuration = tryWebrtc ? 10000 : (tryAddresses ? 1000 : 0);
|
||||
console.log('waiting', waitDuration);
|
||||
if (waitDuration) {
|
||||
// give the peer to peers a second, but then try connecting directly.
|
||||
@@ -448,6 +597,7 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
await once(check, 'open');
|
||||
return {
|
||||
ready: check,
|
||||
address: explicitBaseUrl,
|
||||
connectionType: 'http',
|
||||
};
|
||||
})());
|
||||
@@ -455,6 +605,9 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
const any = Promise.any(promises);
|
||||
let { ready, connectionType, address, rpcPeer } = await any;
|
||||
|
||||
if (tryWebrtc && connectionType !== 'webrtc')
|
||||
localStorage.setItem(webrtcLastFailedKey, Date.now().toString());
|
||||
|
||||
console.log('connected', connectionType, address)
|
||||
|
||||
socket = ready;
|
||||
@@ -572,9 +725,18 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
pluginHostAPI: undefined,
|
||||
rtcConnectionManagement,
|
||||
browserSignalingSession,
|
||||
authorization,
|
||||
queryToken,
|
||||
rpcPeer,
|
||||
loginResult: {
|
||||
username,
|
||||
token,
|
||||
directAddress,
|
||||
localAddresses,
|
||||
externalAddresses,
|
||||
scryptedCloud,
|
||||
queryToken,
|
||||
authorization,
|
||||
cloudAddress,
|
||||
}
|
||||
}
|
||||
|
||||
socket.on('close', () => {
|
||||
|
||||
4
packages/deferred/package-lock.json
generated
4
packages/deferred/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/rpc",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/rpc",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.4",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/deferred",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.4",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
|
||||
1
packages/deferred/src/async-queue.ts
Symbolic link
1
packages/deferred/src/async-queue.ts
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../common/src/async-queue.ts
|
||||
1
packages/deferred/src/deferred.ts
Symbolic link
1
packages/deferred/src/deferred.ts
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../common/src/deferred.ts
|
||||
@@ -1 +0,0 @@
|
||||
../../../common/src/deferred.ts
|
||||
2
packages/deferred/src/index.ts
Normal file
2
packages/deferred/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './deferred';
|
||||
export * from './async-queue';
|
||||
23
packages/h264-repacketizer/.vscode/launch.json
vendored
Normal file
23
packages/h264-repacketizer/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
// 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": [
|
||||
"${workspaceFolder}/test/test.ts"
|
||||
],
|
||||
"runtimeArgs": [
|
||||
"-r",
|
||||
"ts-node/register"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"protocol": "inspector",
|
||||
"internalConsoleOptions": "openOnSessionStart"
|
||||
}
|
||||
]
|
||||
}
|
||||
300
packages/h264-repacketizer/package-lock.json
generated
300
packages/h264-repacketizer/package-lock.json
generated
@@ -1,16 +1,17 @@
|
||||
{
|
||||
"name": "@scrypted/h264-packetizer",
|
||||
"version": "0.0.6",
|
||||
"name": "@scrypted/h264-repacketizer",
|
||||
"version": "0.0.7",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/h264-packetizer",
|
||||
"version": "0.0.6",
|
||||
"name": "@scrypted/h264-repacketizer",
|
||||
"version": "0.0.7",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
"rimraf": "^4.1.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.7.4"
|
||||
}
|
||||
},
|
||||
@@ -43,12 +44,121 @@
|
||||
"../sdk/types": {
|
||||
"extraneous": true
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
|
||||
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
|
||||
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@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/node": {
|
||||
"version": "18.11.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
|
||||
"integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
|
||||
"integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"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/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/rimraf": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.1.1.tgz",
|
||||
@@ -64,6 +174,49 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"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": "4.7.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
|
||||
@@ -76,26 +229,165 @@
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
},
|
||||
"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/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"
|
||||
}
|
||||
},
|
||||
"@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
|
||||
"dev": true
|
||||
},
|
||||
"@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": "18.11.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
|
||||
"integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
|
||||
"integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
|
||||
"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==",
|
||||
"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
|
||||
},
|
||||
"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
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.1.1.tgz",
|
||||
"integrity": "sha512-Z4Y81w8atcvaJuJuBB88VpADRH66okZAuEm+Jtaufa+s7rZmIz+Hik2G53kGaNytE7lsfXyWktTmfVz0H9xuDg==",
|
||||
"dev": true
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@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.7.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
|
||||
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
|
||||
"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
|
||||
},
|
||||
"yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/h264-repacketizer",
|
||||
"version": "0.0.6",
|
||||
"version": "0.0.7",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
@@ -14,6 +14,7 @@
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
"rimraf": "^4.1.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.7.4"
|
||||
}
|
||||
}
|
||||
|
||||
93
packages/h264-repacketizer/test/test.ts
Normal file
93
packages/h264-repacketizer/test/test.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { H264Repacketizer, depacketizeStapA } from '../src/index';
|
||||
import { H264_NAL_TYPE_IDR, H264_NAL_TYPE_PPS, H264_NAL_TYPE_SEI, H264_NAL_TYPE_SPS, H264_NAL_TYPE_STAP_A, RtspServer, getNaluTypesInNalu } from '../../../common/src/rtsp-server';
|
||||
import fs from 'fs';
|
||||
|
||||
import { getNvrSessionStream } from '../../../../nvr/nvr-plugin/src/session-stream';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import { RtpPacket } from '../../../external/werift/packages/rtp/src/rtp/rtp';
|
||||
|
||||
function parse(parameters: string) {
|
||||
const spspps = parameters.split(',');
|
||||
// empty sprop-parameter-sets is apparently a thing:
|
||||
// a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=
|
||||
if (spspps?.length !== 2) {
|
||||
return {
|
||||
sps: undefined,
|
||||
pps: undefined,
|
||||
};
|
||||
}
|
||||
const [sps, pps] = spspps;
|
||||
|
||||
return {
|
||||
sps: Buffer.from(sps, 'base64'),
|
||||
pps: Buffer.from(pps, 'base64'),
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const spspps = parse('Z2QAM6wVFKAoALWQ,aO48sA==');
|
||||
// Z2QAM6wVFKAoALWQ
|
||||
// Z00AMpY1QEABg03BQEFQAAADABAAAAMDKEA=
|
||||
|
||||
|
||||
const repacketizer = new H264Repacketizer(console, 1300, undefined);
|
||||
|
||||
const stream = fs.createReadStream('/Users/koush/Downloads/rtsp/1692537093973.rtsp', {
|
||||
start: 0,
|
||||
highWaterMark: 800000,
|
||||
});
|
||||
|
||||
let rtspParser = new RtspServer(stream as any, '');
|
||||
rtspParser.setupTracks = {
|
||||
'0': {
|
||||
codec: '0',
|
||||
protocol: 'tcp',
|
||||
control: '',
|
||||
destination: 0,
|
||||
},
|
||||
'2': {
|
||||
codec: '2',
|
||||
protocol: 'tcp',
|
||||
control: '',
|
||||
destination: 2,
|
||||
},
|
||||
}
|
||||
for await (const rtspSample of rtspParser.handleRecord()) {
|
||||
if (rtspSample.type !== '0')
|
||||
continue;
|
||||
const rtp = RtpPacket.deSerialize(rtspSample.packet);
|
||||
const nalus = getNaluTypesInNalu(rtp.payload);
|
||||
if (nalus.has(H264_NAL_TYPE_SEI)) {
|
||||
console.warn('SEI', rtp.payload)
|
||||
}
|
||||
if (nalus.has(H264_NAL_TYPE_SPS)) {
|
||||
console.warn('SPS', rtp.payload, spspps.sps)
|
||||
}
|
||||
if (nalus.has(H264_NAL_TYPE_PPS)) {
|
||||
console.warn('PPS', rtp.payload, spspps.sps)
|
||||
}
|
||||
if (nalus.has(H264_NAL_TYPE_STAP_A)) {
|
||||
const parts = depacketizeStapA(rtp.payload);
|
||||
console.log('stapa', parts);
|
||||
for (const part of parts) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (nalus.has(H264_NAL_TYPE_IDR)) {
|
||||
const h264Packetizer = new H264Repacketizer(console, 65535, spspps as any);
|
||||
// offset the stapa packet by -1 so the sequence numbers can be reused.
|
||||
h264Packetizer.extraPackets = -1;
|
||||
const stapas: RtpPacket[] = [];
|
||||
const idr = RtpPacket.deSerialize(rtspSample.packet);
|
||||
h264Packetizer.maybeSendStapACodecInfo(idr, stapas);
|
||||
if (stapas.length === 1) {
|
||||
const stapa = stapas[0].serialize();
|
||||
// console.log(stapa);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
main();
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "ES2019",
|
||||
"target": "ES2020",
|
||||
"noImplicitAny": true,
|
||||
"outDir": "./dist",
|
||||
"esModuleInterop": true,
|
||||
@@ -10,6 +10,7 @@
|
||||
"declaration": true,
|
||||
"resolveJsonModule": true,
|
||||
},
|
||||
"exclude": ["**/node_modules"],
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
|
||||
1
packages/python-client/.gitignore
vendored
Normal file
1
packages/python-client/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.venv
|
||||
16
packages/python-client/.vscode/launch.json
vendored
Normal file
16
packages/python-client/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
// 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": "Python: Current File",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/test.py",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true
|
||||
}
|
||||
]
|
||||
}
|
||||
1
packages/python-client/plugin_remote.py
Symbolic link
1
packages/python-client/plugin_remote.py
Symbolic link
@@ -0,0 +1 @@
|
||||
../../server/python/plugin_remote.py
|
||||
3
packages/python-client/requirements.txt
Normal file
3
packages/python-client/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
python-engineio[asyncio_client]
|
||||
aiohttp
|
||||
aiodns
|
||||
1
packages/python-client/rpc.py
Symbolic link
1
packages/python-client/rpc.py
Symbolic link
@@ -0,0 +1 @@
|
||||
../../server/python/rpc.py
|
||||
1
packages/python-client/rpc_reader.py
Symbolic link
1
packages/python-client/rpc_reader.py
Symbolic link
@@ -0,0 +1 @@
|
||||
../../server/python/rpc_reader.py
|
||||
1
packages/python-client/scrypted_python
Symbolic link
1
packages/python-client/scrypted_python
Symbolic link
@@ -0,0 +1 @@
|
||||
../../sdk/types/scrypted_python
|
||||
151
packages/python-client/test.py
Normal file
151
packages/python-client/test.py
Normal file
@@ -0,0 +1,151 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
from contextlib import nullcontext
|
||||
|
||||
import aiohttp
|
||||
import engineio
|
||||
|
||||
import plugin_remote
|
||||
import rpc_reader
|
||||
from plugin_remote import DeviceManager, MediaManager, SystemManager
|
||||
from scrypted_python.scrypted_sdk import ScryptedInterface, ScryptedStatic
|
||||
|
||||
|
||||
class EioRpcTransport(rpc_reader.RpcTransport):
|
||||
def __init__(self, loop: asyncio.AbstractEventLoop):
|
||||
super().__init__()
|
||||
self.eio = engineio.AsyncClient(ssl_verify=False)
|
||||
self.loop = loop
|
||||
self.write_error: Exception = None
|
||||
self.read_queue = asyncio.Queue()
|
||||
self.write_queue = asyncio.Queue()
|
||||
|
||||
@self.eio.on("message")
|
||||
def on_message(data):
|
||||
self.read_queue.put_nowait(data)
|
||||
|
||||
asyncio.run_coroutine_threadsafe(self.send_loop(), self.loop)
|
||||
|
||||
async def read(self):
|
||||
return await self.read_queue.get()
|
||||
|
||||
async def send_loop(self):
|
||||
while True:
|
||||
data = await self.write_queue.get()
|
||||
try:
|
||||
await self.eio.send(data)
|
||||
except Exception as e:
|
||||
self.write_error = e
|
||||
self.write_queue = None
|
||||
break
|
||||
|
||||
def writeBuffer(self, buffer, reject):
|
||||
async def send():
|
||||
try:
|
||||
if self.write_error:
|
||||
raise self.write_error
|
||||
self.write_queue.put_nowait(buffer)
|
||||
except Exception as e:
|
||||
reject(e)
|
||||
|
||||
asyncio.run_coroutine_threadsafe(send(), self.loop)
|
||||
|
||||
def writeJSON(self, json, reject):
|
||||
return self.writeBuffer(json, reject)
|
||||
|
||||
|
||||
async def connect_scrypted_client(
|
||||
transport: EioRpcTransport,
|
||||
base_url: str,
|
||||
username: str,
|
||||
password: str,
|
||||
plugin_id: str = "@scrypted/core",
|
||||
session: aiohttp.ClientSession | None = None,
|
||||
) -> ScryptedStatic:
|
||||
login_url = f"{base_url}/login"
|
||||
login_body = {
|
||||
"username": username,
|
||||
"password": password,
|
||||
}
|
||||
|
||||
if session:
|
||||
cm = nullcontext(session)
|
||||
else:
|
||||
cm = aiohttp.ClientSession()
|
||||
|
||||
async with cm as _session:
|
||||
async with _session.post(
|
||||
login_url, verify_ssl=False, json=login_body
|
||||
) as response:
|
||||
login_response = await response.json()
|
||||
|
||||
headers = {"Authorization": login_response["authorization"]}
|
||||
|
||||
await transport.eio.connect(
|
||||
base_url,
|
||||
headers=headers,
|
||||
engineio_path=f"/endpoint/{plugin_id}/engine.io/api/",
|
||||
)
|
||||
|
||||
ret = asyncio.Future[ScryptedStatic](loop=transport.loop)
|
||||
peer, peerReadLoop = await rpc_reader.prepare_peer_readloop(
|
||||
transport.loop, transport
|
||||
)
|
||||
peer.params["print"] = print
|
||||
|
||||
def callback(api, pluginId, hostInfo):
|
||||
remote = plugin_remote.PluginRemote(
|
||||
peer, api, pluginId, hostInfo, transport.loop
|
||||
)
|
||||
wrapped = remote.setSystemState
|
||||
|
||||
async def remoteSetSystemState(systemState):
|
||||
await wrapped(systemState)
|
||||
|
||||
async def resolve():
|
||||
sdk = ScryptedStatic()
|
||||
sdk.api = api
|
||||
sdk.remote = remote
|
||||
sdk.systemManager = SystemManager(api, remote.systemState)
|
||||
sdk.deviceManager = DeviceManager(
|
||||
remote.nativeIds, sdk.systemManager
|
||||
)
|
||||
sdk.mediaManager = MediaManager(await api.getMediaManager())
|
||||
ret.set_result(sdk)
|
||||
|
||||
asyncio.run_coroutine_threadsafe(resolve(), transport.loop)
|
||||
|
||||
remote.setSystemState = remoteSetSystemState
|
||||
return remote
|
||||
|
||||
peer.params["getRemote"] = callback
|
||||
asyncio.run_coroutine_threadsafe(peerReadLoop(), transport.loop)
|
||||
|
||||
sdk = await ret
|
||||
return sdk
|
||||
|
||||
|
||||
async def main():
|
||||
transport = EioRpcTransport(asyncio.get_event_loop())
|
||||
sdk = await connect_scrypted_client(
|
||||
transport,
|
||||
"https://localhost:10443",
|
||||
os.environ["SCRYPTED_USERNAME"],
|
||||
os.environ["SCRYPTED_PASSWORD"],
|
||||
)
|
||||
|
||||
for id in sdk.systemManager.getSystemState():
|
||||
device = sdk.systemManager.getDeviceById(id)
|
||||
print(device.name)
|
||||
if ScryptedInterface.OnOff.value in device.interfaces:
|
||||
print(f"OnOff: device is {device.on}")
|
||||
|
||||
await transport.eio.disconnect()
|
||||
os._exit(0)
|
||||
|
||||
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.run_coroutine_threadsafe(main(), loop)
|
||||
loop.run_forever()
|
||||
2
plugins/alexa/.vscode/settings.json
vendored
2
plugins/alexa/.vscode/settings.json
vendored
@@ -1,4 +1,4 @@
|
||||
|
||||
{
|
||||
"scrypted.debugHost": "10.10.0.50",
|
||||
"scrypted.debugHost": "koushik-ubuntu",
|
||||
}
|
||||
7
plugins/alexa/package-lock.json
generated
7
plugins/alexa/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/alexa",
|
||||
"version": "0.2.3",
|
||||
"version": "0.2.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/alexa",
|
||||
"version": "0.2.3",
|
||||
"version": "0.2.7",
|
||||
"dependencies": {
|
||||
"axios": "^1.3.4",
|
||||
"uuid": "^9.0.0"
|
||||
@@ -17,7 +17,8 @@
|
||||
}
|
||||
},
|
||||
"../../sdk": {
|
||||
"version": "0.2.85",
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.2.104",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/alexa",
|
||||
"version": "0.2.4",
|
||||
"version": "0.2.8",
|
||||
"scripts": {
|
||||
"scrypted-setup-project": "scrypted-setup-project",
|
||||
"prescrypted-setup-project": "scrypted-package-json",
|
||||
|
||||
@@ -392,14 +392,21 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
})
|
||||
}
|
||||
|
||||
private setReauthenticateAlert() {
|
||||
const msg: string = "Please reauthenticate by following the directions below.";
|
||||
this.log.a(msg);
|
||||
}
|
||||
|
||||
getAccessToken(): Promise<string> {
|
||||
if (this.accessToken)
|
||||
return this.accessToken;
|
||||
|
||||
this.log.clearAlerts();
|
||||
|
||||
const { tokenInfo } = this.storageSettings.values;
|
||||
|
||||
if (tokenInfo === undefined) {
|
||||
this.log.e("Please reauthenticate by following the directions below.");
|
||||
this.setReauthenticateAlert();
|
||||
throw new Error("'tokenInfo' is undefined");
|
||||
}
|
||||
|
||||
@@ -432,19 +439,19 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
case 'invalid_grant':
|
||||
case 'unauthorized_client':
|
||||
self.console.error(error?.response?.data);
|
||||
self.log.e(error?.response?.data?.error_description);
|
||||
self.log.a(error?.response?.data?.error_description);
|
||||
self.storageSettings.values.tokenInfo = undefined;
|
||||
self.accessToken = undefined;
|
||||
break;
|
||||
|
||||
case 'authorization_pending':
|
||||
self.console.warn(error?.response?.data);
|
||||
self.log.w(error?.response?.data?.error_description);
|
||||
self.log.a(error?.response?.data?.error_description);
|
||||
break;
|
||||
|
||||
case 'expired_token':
|
||||
self.console.warn(error?.response?.data);
|
||||
self.log.w(error?.response?.data?.error_description);
|
||||
self.log.a(error?.response?.data?.error_description);
|
||||
self.accessToken = undefined;
|
||||
break;
|
||||
|
||||
@@ -488,6 +495,8 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
});
|
||||
|
||||
if (accessToken !== undefined) {
|
||||
this.log.clearAlerts();
|
||||
|
||||
try {
|
||||
response.send({
|
||||
"event": {
|
||||
|
||||
@@ -120,7 +120,7 @@ export async function getCameraCapabilities(device: ScryptedDevice): Promise<Dis
|
||||
"interface": "Alexa.RTCSessionController",
|
||||
"version": "3",
|
||||
"configuration": {
|
||||
isFullDuplexAudioSupported: true,
|
||||
"isFullDuplexAudioSupported": true,
|
||||
}
|
||||
} as DiscoveryCapability
|
||||
];
|
||||
|
||||
@@ -7,10 +7,19 @@ import { Response, WebRTCAnswerGeneratedForSessionEvent, WebRTCSessionConnectedE
|
||||
|
||||
export class AlexaSignalingSession implements RTCSignalingSession {
|
||||
constructor(public response: AlexaHttpResponse, public directive: any) {
|
||||
this.options = this.createOptions();
|
||||
this.__proxy_props = { options: this.createOptions() };
|
||||
}
|
||||
|
||||
__proxy_props: { options: RTCSignalingOptions; };
|
||||
options: RTCSignalingOptions;
|
||||
|
||||
async getOptions(): Promise<RTCSignalingOptions> {
|
||||
return {
|
||||
return this.options;
|
||||
}
|
||||
|
||||
private createOptions() {
|
||||
const options: RTCSignalingOptions = {
|
||||
proxy: true,
|
||||
offer: {
|
||||
type: 'offer',
|
||||
@@ -24,7 +33,9 @@ export class AlexaSignalingSession implements RTCSignalingSession {
|
||||
width: 1280,
|
||||
height: 720
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
async createLocalDescription(type: "offer" | "answer", setup: RTCAVSignalingSetup, sendIceCandidate: RTCSignalingSendIceCandidate): Promise<RTCSessionDescriptionInit> {
|
||||
|
||||
@@ -6,11 +6,11 @@ import { supportedTypes } from ".";
|
||||
supportedTypes.set(ScryptedDeviceType.Doorbell, {
|
||||
async discover(device: ScryptedDevice): Promise<Partial<DiscoveryEndpoint>> {
|
||||
let capabilities: any[] = [];
|
||||
let category: DisplayCategory = 'DOORBELL';
|
||||
const displayCategories: DisplayCategory[] = [];
|
||||
|
||||
if (device.interfaces.includes(ScryptedInterface.RTCSignalingChannel)) {
|
||||
capabilities = await getCameraCapabilities(device);
|
||||
category = 'CAMERA';
|
||||
displayCategories.push('CAMERA');
|
||||
}
|
||||
|
||||
if (device.interfaces.includes(ScryptedInterface.BinarySensor)) {
|
||||
@@ -24,8 +24,11 @@ supportedTypes.set(ScryptedDeviceType.Doorbell, {
|
||||
);
|
||||
}
|
||||
|
||||
// Important: If your device is a video doorbell, make sure that you list CAMERA before DOORBELL in the displayCategories list.
|
||||
displayCategories.push('DOORBELL');
|
||||
|
||||
return {
|
||||
displayCategories: [category],
|
||||
displayCategories,
|
||||
capabilities
|
||||
};
|
||||
},
|
||||
@@ -38,6 +41,9 @@ supportedTypes.set(ScryptedDeviceType.Doorbell, {
|
||||
if (response)
|
||||
return response;
|
||||
|
||||
if (eventDetails.eventInterface === ScryptedInterface.BinarySensor && eventData === false)
|
||||
return {};
|
||||
|
||||
if (eventDetails.eventInterface === ScryptedInterface.BinarySensor && eventData === true)
|
||||
return {
|
||||
event: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"module": "Node16",
|
||||
"target": "ES2021",
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "Node16",
|
||||
|
||||
199
plugins/amcrest/package-lock.json
generated
199
plugins/amcrest/package-lock.json
generated
@@ -1,26 +1,25 @@
|
||||
{
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.121",
|
||||
"lockfileVersion": 2,
|
||||
"version": "0.0.128",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.121",
|
||||
"version": "0.0.128",
|
||||
"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.2"
|
||||
"multiparty": "^4.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.15.11"
|
||||
"@types/node": "^18.16.18"
|
||||
}
|
||||
},
|
||||
"../../common": {
|
||||
"name": "@scrypted/common",
|
||||
"version": "1.0.1",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
@@ -35,8 +34,7 @@
|
||||
}
|
||||
},
|
||||
"../../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.2.87",
|
||||
"version": "0.2.103",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
@@ -71,9 +69,6 @@
|
||||
"typedoc": "^0.23.21"
|
||||
}
|
||||
},
|
||||
"../sdk": {
|
||||
"extraneous": true
|
||||
},
|
||||
"node_modules/@koush/axios-digest-auth": {
|
||||
"version": "0.8.5",
|
||||
"resolved": "https://registry.npmjs.org/@koush/axios-digest-auth/-/axios-digest-auth-0.8.5.tgz",
|
||||
@@ -100,9 +95,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
|
||||
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q=="
|
||||
"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",
|
||||
@@ -120,15 +115,15 @@
|
||||
"node_modules/depd": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
|
||||
"integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.14.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
||||
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
|
||||
"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",
|
||||
@@ -145,15 +140,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz",
|
||||
"integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==",
|
||||
"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.0"
|
||||
"toidentifier": "1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
@@ -165,11 +160,11 @@
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/multiparty": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/multiparty/-/multiparty-4.2.2.tgz",
|
||||
"integrity": "sha512-NtZLjlvsjcoGrzojtwQwn/Tm90aWJ6XXtPppYF4WmOk/6ncdwMMKggFY2NlRRN9yiCEIVxpOfPWahVEG2HAG8Q==",
|
||||
"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.0",
|
||||
"http-errors": "~1.8.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"uid-safe": "2.1.5"
|
||||
},
|
||||
@@ -180,7 +175,7 @@
|
||||
"node_modules/random-bytes": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
|
||||
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=",
|
||||
"integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
@@ -212,15 +207,15 @@
|
||||
"node_modules/statuses": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
|
||||
"integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
|
||||
"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"
|
||||
}
|
||||
@@ -236,147 +231,5 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@koush/axios-digest-auth": {
|
||||
"version": "0.8.5",
|
||||
"resolved": "https://registry.npmjs.org/@koush/axios-digest-auth/-/axios-digest-auth-0.8.5.tgz",
|
||||
"integrity": "sha512-EZMM0gMJ3hMUD4EuUqSwP6UGt5Vmw2TZtY7Ypec55AnxkExSXM0ySgPtqkAcnL43g1R27yAg/dQL7dRTLMqO3Q==",
|
||||
"requires": {
|
||||
"auth-header": "^1.0.0",
|
||||
"axios": "^0.21.4"
|
||||
}
|
||||
},
|
||||
"@scrypted/common": {
|
||||
"version": "file:../../common",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"@scrypted/sdk": {
|
||||
"version": "file:../../sdk",
|
||||
"requires": {
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/stringify-object": "^4.0.0",
|
||||
"adm-zip": "^0.4.13",
|
||||
"axios": "^0.21.4",
|
||||
"babel-loader": "^9.1.0",
|
||||
"babel-plugin-const-enum": "^1.1.0",
|
||||
"esbuild": "^0.15.9",
|
||||
"ncp": "^2.0.0",
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"stringify-object": "^3.3.0",
|
||||
"tmp": "^0.2.1",
|
||||
"ts-loader": "^9.4.2",
|
||||
"ts-node": "^10.4.0",
|
||||
"typedoc": "^0.23.21",
|
||||
"typescript": "^4.9.4",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-bundle-analyzer": "^4.5.0"
|
||||
}
|
||||
},
|
||||
"@types/multiparty": {
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/multiparty/-/multiparty-0.0.33.tgz",
|
||||
"integrity": "sha512-Il6cJUpSqgojT7NxbVJUvXkCblm50/yEJYtblISDsNIeNYf4yMAhdizzidUk6h8pJ8yhwK/3Fkb+3Dwcgtwl8w==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "18.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
|
||||
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q=="
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.21.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
|
||||
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.14.0"
|
||||
}
|
||||
},
|
||||
"depd": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
||||
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w=="
|
||||
},
|
||||
"http-errors": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz",
|
||||
"integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==",
|
||||
"requires": {
|
||||
"depd": "~1.1.2",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": ">= 1.5.0 < 2",
|
||||
"toidentifier": "1.0.0"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"multiparty": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/multiparty/-/multiparty-4.2.2.tgz",
|
||||
"integrity": "sha512-NtZLjlvsjcoGrzojtwQwn/Tm90aWJ6XXtPppYF4WmOk/6ncdwMMKggFY2NlRRN9yiCEIVxpOfPWahVEG2HAG8Q==",
|
||||
"requires": {
|
||||
"http-errors": "~1.8.0",
|
||||
"safe-buffer": "5.2.1",
|
||||
"uid-safe": "2.1.5"
|
||||
}
|
||||
},
|
||||
"random-bytes": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
|
||||
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"setprototypeof": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
||||
},
|
||||
"statuses": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
|
||||
},
|
||||
"toidentifier": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
||||
},
|
||||
"uid-safe": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
|
||||
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
|
||||
"requires": {
|
||||
"random-bytes": "~1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.121",
|
||||
"version": "0.0.128",
|
||||
"description": "Amcrest Plugin for Scrypted",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache",
|
||||
@@ -39,9 +39,9 @@
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"@types/multiparty": "^0.0.33",
|
||||
"multiparty": "^4.2.2"
|
||||
"multiparty": "^4.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.15.11"
|
||||
"@types/node": "^18.16.18"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,16 @@ export class AmcrestCameraClient {
|
||||
});
|
||||
}
|
||||
|
||||
async reboot() {
|
||||
const response = await this.digestAuth.request({
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
method: "GET",
|
||||
responseType: 'text',
|
||||
url: `http://${this.ip}/cgi-bin/magicBox.cgi?action=reboot`,
|
||||
});
|
||||
return response.data as string;
|
||||
}
|
||||
|
||||
async checkTwoWayAudio() {
|
||||
const response = await this.digestAuth.request({
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
@@ -61,6 +71,7 @@ export class AmcrestCameraClient {
|
||||
method: "GET",
|
||||
responseType: 'arraybuffer',
|
||||
url: `http://${this.ip}/cgi-bin/snapshot.cgi`,
|
||||
timeout: 60000,
|
||||
});
|
||||
|
||||
return Buffer.from(response.data);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user