Compare commits

...

28 Commits

Author SHA1 Message Date
Koushik Dutta
1fb0c01e7e postbeta 2024-06-04 15:53:17 -07:00
Koushik Dutta
014d7b35ac server: ensure plugins get restarted if failing during reload 2024-06-04 15:53:04 -07:00
Koushik Dutta
b08267dab0 server: beta 2024-06-04 13:59:58 -07:00
Koushik Dutta
97d78516f2 postbeta 2024-06-04 13:59:53 -07:00
Koushik Dutta
360c2437c1 postbeta 2024-06-04 13:26:57 -07:00
Koushik Dutta
0b230bfc74 Merge branch 'main' of github.com:koush/scrypted 2024-06-04 12:58:12 -07:00
Koushik Dutta
d25dc8d266 postbeta 2024-06-04 12:57:01 -07:00
Koushik Dutta
5f4d1e99cd postbeta 2024-06-04 12:43:17 -07:00
Koushik Dutta
ee38ef7817 Update bug_report.md 2024-06-04 08:32:06 -07:00
Koushik Dutta
80af38d3e1 Merge branch 'main' of github.com:koush/scrypted 2024-06-03 23:34:05 -07:00
Koushik Dutta
2f19866f05 predict: relax face threshold 2024-06-03 23:34:01 -07:00
Long Zheng
cf1c500e9d common: Enable TypeScript strict for packages/auth-fetch (#1493)
* Add tsconfig strict to packages/auth-fetch

* Refactor switch case

* Revert "Refactor switch case"

This reverts commit b5004664bb.

* Revert switch changes
2024-06-03 17:48:38 -07:00
Koushik Dutta
9a770e9dc9 predict: update models 2024-06-03 15:08:58 -07:00
Koushik Dutta
6dbb8863a0 Merge branch 'main' of github.com:koush/scrypted 2024-06-03 10:38:44 -07:00
Koushik Dutta
5eac8d0ab9 predict: lock opencv version,
roll back to 9c flt
2024-06-03 10:38:36 -07:00
Long Zheng
272bad8f29 cli: Enable TypeScript strict for packages/cli (#1494)
* Enable strict mode on packages/cli

* Fix condition
2024-06-03 10:34:18 -07:00
Koushik Dutta
83a3352862 predict: extract rough text scores 2024-06-02 13:33:54 -07:00
Koushik Dutta
4d5a693208 core: add labels to detection preview 2024-06-02 08:07:48 -07:00
Koushik Dutta
70e7f944c0 postrelease 2024-06-01 22:02:17 -07:00
Koushik Dutta
5a52c03a3d postrelease 2024-06-01 20:09:34 -07:00
Koushik Dutta
f9f597ef01 server: guard entire plugin load block 2024-06-01 13:07:55 -07:00
Koushik Dutta
2e07788c0c server: log plugin load failure 2024-06-01 13:05:56 -07:00
Koushik Dutta
9c0fbc1cb6 common: listenZeroSingleClient configurable timeout 2024-06-01 09:44:51 -07:00
Koushik Dutta
239d49899d unifi-protect: fix id remapping 2024-06-01 09:19:32 -07:00
Koushik Dutta
2d3589b5a3 unifi-protect: fix id remapping 2024-06-01 08:49:37 -07:00
Koushik Dutta
96ec465a38 unifi: more logging 2024-06-01 08:07:24 -07:00
Koushik Dutta
5bb6b87c7d predict: yolov10m 2024-05-31 15:17:24 -07:00
Koushik Dutta
fcfedccaf8 postrelease 2024-05-31 14:01:24 -07:00
42 changed files with 244 additions and 269 deletions

View File

@@ -13,11 +13,11 @@ Before opening an issue, view the device's Console logs in the Scrypted Manageme
**DO NOT OPEN ISSUES FOR ANY OF THE FOLLOWING:**
* Server setup assistance. Use Discord, Reddit, or Github Discussions.
* Hardware setup assistance. Use Discord, Reddit, or Github Discussions.
* Server or hardware setup assistance. Use Discord, Reddit, or Github Discussions.
* Feature Requests. Use Discord, Reddit, or Github Discussions.
* Packet loss in your camera logs. This is wifi/network congestion.
* HomeKit weirdness. See HomeKit troubleshooting guide.
* Release schedules or timelines. Releases are rolled out unevenly across the different server platforms.
However, if something **was working**, and is now **no longer working**, you may create a Github issue.
Created issues that do not meet these requirements or are improperly filled out will be immediately closed.

View File

@@ -70,7 +70,7 @@ async function getAuth(options: AuthFetchOptions, url: string | URL, method: str
export function createAuthFetch<B, M>(
h: fetcher<B, M>,
parser: (body: M, responseType: HttpFetchResponseType) => Promise<any>
parser: (body: M, responseType: HttpFetchResponseType | undefined) => Promise<any>
) {
const authHttpFetch = async <T extends HttpFetchOptions<B>>(options: T & AuthFetchOptions): ReturnType<typeof h<T>> => {
const method = getFetchMethod(options);
@@ -99,7 +99,7 @@ export function createAuthFetch<B, M>(
};
}
let authenticateHeaders: string | string[] = initialResponse.headers.get('www-authenticate');
let authenticateHeaders: string | string[] | null = initialResponse.headers.get('www-authenticate');
if (!authenticateHeaders)
throw new Error('Did not find WWW-Authenticate header.');

View File

@@ -9,6 +9,7 @@
"inlineSources": true,
"declaration": true,
"resolveJsonModule": true,
"strict": true
},
"include": [
"src/**/*"

View File

@@ -10,7 +10,7 @@
"license": "ISC",
"dependencies": {
"@scrypted/client": "^1.3.3",
"@scrypted/types": "^0.2.99",
"@scrypted/types": "^0.3.30",
"engine.io-client": "^6.5.3",
"readline-sync": "^1.4.10",
"semver": "^7.5.4",
@@ -101,15 +101,11 @@
"rimraf": "^5.0.5"
}
},
"node_modules/@scrypted/client/node_modules/@scrypted/types": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.3.4.tgz",
"integrity": "sha512-k/YMx8lIWOkePgXfKW9POr12mb+erFU2JKxO7TW92GyW8ojUWw9VOc0PK6O9bybi0vhsEnvMFkO6pO6bAonsVA=="
},
"node_modules/@scrypted/types": {
"version": "0.2.99",
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.2.99.tgz",
"integrity": "sha512-2J1FH7tpAW5X3rgA70gJ+z0HFM90c/tBA+JXdP1vI1d/0yVmh9TSxnHoCuADN4R2NQXHmoZ6Nbds9kKAQ/25XQ=="
"version": "0.3.30",
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.3.30.tgz",
"integrity": "sha512-1k+JVSR6WSNmE/5mLdqfrTmV3uRbvZp0OwKb8ikNi39ysBuC000tQGcEdXZqhYqRgWdhDTWtxXe9XsYoAZGKmA==",
"license": "ISC"
},
"node_modules/@socket.io/component-emitter": {
"version": "3.1.0",

View File

@@ -17,7 +17,7 @@
"license": "ISC",
"dependencies": {
"@scrypted/client": "^1.3.3",
"@scrypted/types": "^0.2.99",
"@scrypted/types": "^0.3.30",
"engine.io-client": "^6.5.3",
"readline-sync": "^1.4.10",
"semver": "^7.5.4",

View File

@@ -160,11 +160,11 @@ async function main() {
const ffmpegInput = await sdk.mediaManager.convertMediaObjectToJSON<FFmpegInput>(await pendingResult, ScryptedMimeTypes.FFmpegInput);
if (ffmpegInput.url && ffmpegInput.urls?.[0]) {
const url = new URL(ffmpegInput.url);
if (url.hostname === '127.0.0.1' && ffmpegInput.urls?.[0]) {
ffmpegInput.inputArguments = ffmpegInput.inputArguments.map(i => i === ffmpegInput.url ? ffmpegInput.urls?.[0] : i);
if (url.hostname === '127.0.0.1' && ffmpegInput.urls?.[0] && ffmpegInput.inputArguments) {
ffmpegInput.inputArguments = ffmpegInput.inputArguments.map(i => i === ffmpegInput.url && ffmpegInput.urls ? ffmpegInput.urls?.[0] : i);
}
}
const args = [...ffmpegInput.inputArguments];
const args = ffmpegInput.inputArguments ? [...ffmpegInput.inputArguments] : [];
if (ffmpegInput.h264FilterArguments)
args.push(...ffmpegInput.h264FilterArguments);
console.log('ffplay', ...args);

View File

@@ -90,7 +90,13 @@ export async function installServe(installVersion: string, ignoreError?: boolean
const installJson = path.join(installDir, 'install.json');
try {
const { version } = JSON.parse(fs.readFileSync(installJson).toString());
if (semver.parse(process.version).major !== semver.parse(version).major)
const processSemver = semver.parse(process.version);
if (!processSemver)
throw new Error('error parsing process version');
const installSemver = semver.parse(version);
if (!installSemver)
throw new Error('error parsing install.json version');
if (processSemver.major !== installSemver.major)
throw new Error('mismatch');
}
catch (e) {
@@ -111,16 +117,32 @@ export async function installServe(installVersion: string, ignoreError?: boolean
}
export async function serveMain(installVersion?: string) {
let install = !!installVersion;
const options = ((): { install: true; version: string } | { install: false } => {
if (installVersion) {
console.log(`Installing @scrypted/server@${installVersion}`);
return {
install: true,
version: installVersion
};
}
if (!fs.existsSync('node_modules/@scrypted/server')) {
console.log('Package @scrypted/server not found. Installing.');
return {
install: true,
version: 'latest',
};
}
return {
install: false,
}
})();
const { installDir, volume } = cwdInstallDir();
if (!fs.existsSync('node_modules/@scrypted/server')) {
install = true;
installVersion ||= 'latest';
console.log('Package @scrypted/server not found. Installing.');
}
if (install) {
await installServe(installVersion, true);
if (options.install) {
await installServe(options.version, true);
}
// todo: remove at some point after core lxc updater rolls out.

View File

@@ -9,6 +9,7 @@
"inlineSources": true,
"declaration": true,
"moduleResolution": "Node16",
"strict": true
},
"include": [
"src/**/*"

View File

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

View File

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

View File

@@ -161,10 +161,10 @@ export default {
let t = ``;
let toffset = 0;
if (detection.score && detection.className !== 'motion') {
t += `<tspan x='${x}' dy='${toffset}em'>${Math.round(detection.score * 100) / 100}</tspan>`
t += `<tspan x='${x}' dy='${toffset}em'>${Math.round((detection.labelScore || detection.score) * 100) / 100}</tspan>`
toffset -= 1.2;
}
const tname = detection.className + (detection.id ? `: ${detection.id}` : '')
const tname = (detection.label || detection.className) + (detection.id ? `: ${detection.id}` : '')
t += `<tspan x='${x}' dy='${toffset}em'>${tname}</tspan>`
const fs = 20;

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/coreml",
"version": "0.1.53",
"version": "0.1.57",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/coreml",
"version": "0.1.53",
"version": "0.1.57",
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
}

View File

@@ -42,5 +42,5 @@
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
},
"version": "0.1.53"
"version": "0.1.57"
}

View File

@@ -26,17 +26,14 @@ predictExecutor = concurrent.futures.ThreadPoolExecutor(1, "CoreML-Predict")
availableModels = [
"Default",
"scrypted_yolov10m_320",
"scrypted_yolov10n_320",
"scrypted_yolov10n",
"scrypted_yolo_nas_s_320",
"scrypted_yolov9e_320",
"scrypted_yolov9c_320",
"scrypted_yolov9c",
"scrypted_yolov6n_320",
"scrypted_yolov6n",
"scrypted_yolov6s_320",
"scrypted_yolov6s",
"scrypted_yolov8n_320",
"scrypted_yolov8n",
"ssdlite_mobilenet_v2",
"yolov4-tiny",
]
@@ -80,7 +77,7 @@ class CoreMLPlugin(PredictPlugin, scrypted_sdk.Settings, scrypted_sdk.DeviceProv
self.storage.setItem("model", "Default")
model = "scrypted_yolov9c_320"
self.yolo = "yolo" in model
self.scrypted_yolov10n = "scrypted_yolov10n" in model
self.scrypted_yolov10n = "scrypted_yolov10" in model
self.scrypted_yolo_nas = "scrypted_yolo_nas" in model
self.scrypted_yolo = "scrypted_yolo" in model
self.scrypted_model = "scrypted" in model

View File

@@ -1 +1 @@
opencv-python
opencv-python==4.9.0.80

View File

@@ -1,3 +1,2 @@
# 2024-04-23 - modify timestamp to force pip reinstall
coremltools==7.1
Pillow>=5.4.1

View File

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

View File

@@ -42,5 +42,5 @@
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
},
"version": "0.1.92"
"version": "0.1.95"
}

View File

@@ -29,17 +29,13 @@ except:
availableModels = [
"Default",
"scrypted_yolov10m_320",
"scrypted_yolov10n_320",
"scrypted_yolov10n",
"scrypted_yolo_nas_s_320",
"scrypted_yolov6n_320",
"scrypted_yolov6n",
"scrypted_yolov6s_320",
"scrypted_yolov6s",
"scrypted_yolov9c_320",
"scrypted_yolov9c",
"scrypted_yolov8n_320",
"scrypted_yolov8n",
]
def parse_labels(names):
@@ -59,7 +55,7 @@ class ONNXPlugin(
if model == "Default" or model not in availableModels:
if model != "Default":
self.storage.setItem("model", "Default")
model = "scrypted_yolov8n_320"
model = "scrypted_yolov10m_320"
self.yolo = "yolo" in model
self.scrypted_yolov10 = "scrypted_yolov10" in model
self.scrypted_yolo_nas = "scrypted_yolo_nas" in model
@@ -72,7 +68,7 @@ class ONNXPlugin(
model_version = "v2"
onnxfile = self.downloadFile(
f"https://raw.githubusercontent.com/koush/onnx-models/main/{model}/{onnxmodel}.onnx",
f"https://github.com/koush/onnx-models/raw/main/{model}/{onnxmodel}.onnx",
f"{model_version}/{model}/{onnxmodel}.onnx",
)

View File

@@ -23,7 +23,7 @@ class ONNXFaceRecognition(FaceRecognizeDetection):
onnxmodel = "best" if "scrypted" in model else model
model_version = "v1"
onnxfile = self.downloadFile(
f"https://raw.githubusercontent.com/koush/onnx-models/main/{model}/{onnxmodel}.onnx",
f"https://github.com/koush/onnx-models/raw/main/{model}/{onnxmodel}.onnx",
f"{model_version}/{model}/{onnxmodel}.onnx",
)
print(onnxfile)

View File

@@ -23,7 +23,7 @@ class ONNXTextRecognition(TextRecognition):
onnxmodel = model
model_version = "v3"
onnxfile = self.downloadFile(
f"https://raw.githubusercontent.com/koush/onnx-models/main/{model}/{onnxmodel}.onnx",
f"https://github.com/koush/onnx-models/raw/main/{model}/{onnxmodel}.onnx",
f"{model_version}/{model}/{onnxmodel}.onnx",
)
print(onnxfile)

View File

@@ -1 +1 @@
opencv-python
opencv-python==4.9.0.80

View File

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

View File

@@ -42,5 +42,5 @@
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
},
"version": "0.1.88"
"version": "0.1.96"
}

View File

@@ -30,17 +30,14 @@ prepareExecutor = concurrent.futures.ThreadPoolExecutor(1, "OpenVINO-Prepare")
availableModels = [
"Default",
"scrypted_yolov10m_320",
"scrypted_yolov10s_320",
"scrypted_yolov10n_320",
"scrypted_yolov10n",
"scrypted_yolo_nas_s_320",
"scrypted_yolov6n_320",
"scrypted_yolov6n",
"scrypted_yolov6s_320",
"scrypted_yolov6s",
"scrypted_yolov9c_320",
"scrypted_yolov9c",
"scrypted_yolov8n_320",
"scrypted_yolov8n",
"ssd_mobilenet_v1_coco",
"ssdlite_mobilenet_v2",
"yolo-v3-tiny-tf",
@@ -138,7 +135,7 @@ class OpenVINOPlugin(
if model == "Default" or model not in availableModels:
if model != "Default":
self.storage.setItem("model", "Default")
model = "scrypted_yolov8n_320"
model = "scrypted_yolov10n_320"
self.yolo = "yolo" in model
self.scrypted_yolov10 = "scrypted_yolov10" in model
self.scrypted_yolo_nas = "scrypted_yolo_nas" in model
@@ -152,31 +149,31 @@ class OpenVINOPlugin(
model_version = "v5"
xmlFile = self.downloadFile(
f"https://raw.githubusercontent.com/koush/openvino-models/main/{model}/{precision}/{ovmodel}.xml",
f"https://github.com/koush/openvino-models/raw/main/{model}/{precision}/{ovmodel}.xml",
f"{model_version}/{model}/{precision}/{ovmodel}.xml",
)
binFile = self.downloadFile(
f"https://raw.githubusercontent.com/koush/openvino-models/main/{model}/{precision}/{ovmodel}.bin",
f"https://github.com/koush/openvino-models/raw/main/{model}/{precision}/{ovmodel}.bin",
f"{model_version}/{model}/{precision}/{ovmodel}.bin",
)
if self.scrypted_yolo_nas:
labelsFile = self.downloadFile(
"https://raw.githubusercontent.com/koush/openvino-models/main/scrypted_nas_labels.txt",
"https://github.com/koush/openvino-models/raw/main/scrypted_nas_labels.txt",
"scrypted_nas_labels.txt",
)
elif self.scrypted_model:
labelsFile = self.downloadFile(
"https://raw.githubusercontent.com/koush/openvino-models/main/scrypted_labels.txt",
"https://github.com/koush/openvino-models/raw/main/scrypted_labels.txt",
"scrypted_labels.txt",
)
elif self.yolo:
labelsFile = self.downloadFile(
"https://raw.githubusercontent.com/koush/openvino-models/main/coco_80cl.txt",
"https://github.com/koush/openvino-models/raw/main/coco_80cl.txt",
"coco_80cl.txt",
)
else:
labelsFile = self.downloadFile(
"https://raw.githubusercontent.com/koush/openvino-models/main/coco_labels.txt",
"https://github.com/koush/openvino-models/raw/main/coco_labels.txt",
"coco_labels.txt",
)

View File

@@ -26,11 +26,11 @@ class OpenVINOFaceRecognition(FaceRecognizeDetection):
precision = self.plugin.precision
model_version = "v5"
xmlFile = self.downloadFile(
f"https://raw.githubusercontent.com/koush/openvino-models/main/{model}/{precision}/{ovmodel}.xml",
f"https://github.com/koush/openvino-models/raw/main/{model}/{precision}/{ovmodel}.xml",
f"{model_version}/{model}/{precision}/{ovmodel}.xml",
)
binFile = self.downloadFile(
f"https://raw.githubusercontent.com/koush/openvino-models/main/{model}/{precision}/{ovmodel}.bin",
f"https://github.com/koush/openvino-models/raw/main/{model}/{precision}/{ovmodel}.bin",
f"{model_version}/{model}/{precision}/{ovmodel}.bin",
)
print(xmlFile, binFile)

View File

@@ -25,11 +25,11 @@ class OpenVINOTextRecognition(TextRecognition):
precision = self.plugin.precision
model_version = "v5"
xmlFile = self.downloadFile(
f"https://raw.githubusercontent.com/koush/openvino-models/main/{model}/{precision}/{ovmodel}.xml",
f"https://github.com/koush/openvino-models/raw/main/{model}/{precision}/{ovmodel}.xml",
f"{model_version}/{model}/{precision}/{ovmodel}.xml",
)
binFile = self.downloadFile(
f"https://raw.githubusercontent.com/koush/openvino-models/main/{model}/{precision}/{ovmodel}.bin",
f"https://github.com/koush/openvino-models/raw/main/{model}/{precision}/{ovmodel}.bin",
f"{model_version}/{model}/{precision}/{ovmodel}.bin",
)
print(xmlFile, binFile)

View File

@@ -1 +1 @@
opencv-python
opencv-python==4.9.0.80

View File

@@ -38,6 +38,7 @@ def getDetBoxes_core(textmap, linkmap, text_threshold, link_threshold, low_text,
nLabels, labels, stats, centroids = cv2.connectedComponentsWithStats(text_score_comb.astype(np.uint8), connectivity=4)
det = []
scores = []
mapper = []
for k in range(1,nLabels):
# size filtering
@@ -45,7 +46,8 @@ def getDetBoxes_core(textmap, linkmap, text_threshold, link_threshold, low_text,
if size < 10: continue
# thresholding
if np.max(textmap[labels==k]) < text_threshold: continue
score = np.max(textmap[labels==k])
if score < text_threshold: continue
# make segmentation map
segmap = np.zeros(textmap.shape, dtype=np.uint8)
@@ -89,8 +91,9 @@ def getDetBoxes_core(textmap, linkmap, text_threshold, link_threshold, low_text,
box = np.array(box)
det.append(box)
scores.append(score)
return det, labels, mapper
return det, labels, mapper, scores
def getPoly_core(boxes, labels, mapper, linkmap):
# configs
@@ -241,14 +244,14 @@ def getPoly_core(boxes, labels, mapper, linkmap):
def getDetBoxes(textmap, linkmap, text_threshold, link_threshold, low_text, poly=False, estimate_num_chars=False):
if poly and estimate_num_chars:
raise Exception("Estimating the number of characters not currently supported with poly.")
boxes, labels, mapper = getDetBoxes_core(textmap, linkmap, text_threshold, link_threshold, low_text, estimate_num_chars)
boxes, labels, mapper, scores = getDetBoxes_core(textmap, linkmap, text_threshold, link_threshold, low_text, estimate_num_chars)
if poly:
polys = getPoly_core(boxes, labels, mapper, linkmap)
else:
polys = [None] * len(boxes)
return boxes, polys, mapper
return boxes, polys, mapper, scores
def adjustResultCoordinates(polys, ratio_w, ratio_h, ratio_net = 2):
if len(polys) > 0:

View File

@@ -20,18 +20,16 @@ class FaceRecognizeDetection(PredictPlugin):
def __init__(self, nativeId: str | None = None):
super().__init__(nativeId=nativeId)
self.inputheight = 320
self.inputwidth = 320
self.inputheight = 640
self.inputwidth = 640
self.labels = {
0: "face",
1: "plate",
2: "text",
}
self.loop = asyncio.get_event_loop()
self.minThreshold = 0.7
self.minThreshold = 0.2
self.detectModel = self.downloadModel("scrypted_yolov8n_flt_320")
self.detectModel = self.downloadModel("scrypted_yolov10n_face")
self.faceModel = self.downloadModel("inception_resnet_v1")
def downloadModel(self, model: str):
@@ -49,7 +47,7 @@ class FaceRecognizeDetection(PredictPlugin):
async def detect_once(self, input: Image.Image, settings: Any, src_size, cvss):
results = await self.predictDetectModel(input)
objs = yolo.parse_yolov9(results)
objs = yolo.parse_yolov10(results)
ret = self.create_detection_result(objs, src_size, cvss)
return ret

View File

@@ -65,14 +65,14 @@ class TextRecognition(PredictPlugin):
low_text = 0.4
poly = False
boxes_list, polys_list = [], []
boxes_list, polys_list, scores_list = [], [], []
for out in y:
# make score and link map
score_text = out[:, :, 0]
score_link = out[:, :, 1]
# Post-processing
boxes, polys, mapper = getDetBoxes(
boxes, polys, mapper, scores = getDetBoxes(
score_text,
score_link,
text_threshold,
@@ -96,18 +96,19 @@ class TextRecognition(PredictPlugin):
if polys[k] is None:
polys[k] = boxes[k]
boxes_list.append(boxes)
scores_list.append(scores)
polys_list.append(polys)
preds: List[Prediction] = []
for boxes in boxes_list:
for box in boxes:
for boxes, scores in zip(boxes_list, scores_list):
for box, score in zip(boxes, scores):
tl, tr, br, bl = box
l = min(tl[0], bl[0])
t = min(tl[1], tr[1])
r = max(tr[0], br[0])
b = max(bl[1], br[1])
pred = Prediction(0, 1, Rectangle(l, t, r, b))
pred = Prediction(0, float(score), Rectangle(l, t, r, b))
preds.append(pred)
return self.create_detection_result(preds, src_size, cvss)
@@ -121,18 +122,19 @@ class TextRecognition(PredictPlugin):
futures: List[Future] = []
boundingBoxes = [d["boundingBox"] for d in detections]
boundingBoxes, scores = [d["boundingBox"] for d in detections], [d["score"] for d in detections]
if not len(boundingBoxes):
return ret
text_groups = find_adjacent_groups(boundingBoxes)
text_groups = find_adjacent_groups(boundingBoxes, scores)
detections = []
for group in text_groups:
boundingBox = group["union"]
score = group["score"]
d: ObjectDetectionResult = {
"boundingBox": boundingBox,
"score": 1,
"score": score,
"className": "text",
}
futures.append(

View File

@@ -43,30 +43,31 @@ def are_boxes_adjacent(box1: BoundingBox, box2: BoundingBox):
return False
def find_adjacent_groups(boxes: List[BoundingBox]) -> List[dict]:
def find_adjacent_groups(boxes: List[BoundingBox], scores: List[float]) -> List[dict]:
groups = []
# sort boxes left to right
boxes = sorted(boxes, key=lambda box: box[0])
for box in boxes:
for index, box in enumerate(boxes):
added_to_group = False
for group in groups:
for other_box in group["boxes"]:
if are_boxes_adjacent(box, other_box):
group["boxes"].append(box)
group["scores"].append(scores[index])
added_to_group = True
break
if added_to_group:
break
if not added_to_group:
groups.append({"boxes": [box], "skew_angle": 0})
groups.append({"boxes": [box], "scores": [scores[index]], "skew_angle": 0})
# Calculate the skew angle of each group
for group in groups:
boxes = group["boxes"]
group["union"] = union_boxes(boxes)
if len(boxes) -1 :
if len(boxes) - 1:
lm = (boxes[0][1] + boxes[0][3]) / 2
rm = (boxes[-1][1] + boxes[-1][3]) / 2
dx = (boxes[-1][0]) - (boxes[0][0] + boxes[0][2])
@@ -78,7 +79,10 @@ def find_adjacent_groups(boxes: List[BoundingBox]) -> List[dict]:
group['skew_angle'] = math.atan2(rm - lm, dx) * 2
# pad this box by a few pixels
group['union'] = (group['union'][0] - pad_height, group['union'][1] - pad_height, group['union'][2] + pad_height * 2, group['union'][3] + pad_height * 2)
# average the scores
group['score'] = sum(group['scores']) / len(group['scores'])
else:
group['skew_angle'] = 0
group['score'] = group['scores'][0]
return groups

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/unifi-protect",
"version": "0.0.146",
"version": "0.0.149",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/unifi-protect",
"version": "0.0.146",
"version": "0.0.149",
"license": "Apache",
"dependencies": {
"@koush/unifi-protect": "file:../../external/unifi-protect",
@@ -27,12 +27,12 @@
"dependencies": {
"@scrypted/sdk": "file:../sdk",
"@scrypted/server": "file:../server",
"http-auth-utils": "^3.0.2",
"node-fetch-commonjs": "^3.1.1",
"typescript": "^4.4.3"
"http-auth-utils": "^5.0.1",
"typescript": "^5.3.3"
},
"devDependencies": {
"@types/node": "^16.9.0"
"@types/node": "^20.11.0",
"ts-node": "^10.9.2"
}
},
"../../external/unifi-protect": {
@@ -61,12 +61,12 @@
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.2.103",
"version": "0.3.31",
"license": "ISC",
"dependencies": {
"@babel/preset-typescript": "^7.18.6",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"axios": "^1.6.5",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.15.9",
@@ -260,10 +260,10 @@
"requires": {
"@scrypted/sdk": "file:../sdk",
"@scrypted/server": "file:../server",
"@types/node": "^16.9.0",
"http-auth-utils": "^3.0.2",
"node-fetch-commonjs": "^3.1.1",
"typescript": "^4.4.3"
"@types/node": "^20.11.0",
"http-auth-utils": "^5.0.1",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
}
},
"@scrypted/sdk": {
@@ -273,7 +273,7 @@
"@types/node": "^18.11.18",
"@types/stringify-object": "^4.0.0",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"axios": "^1.6.5",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.15.9",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/unifi-protect",
"version": "0.0.146",
"version": "0.0.149",
"description": "Unifi Protect Plugin for Scrypted",
"author": "Scrypted",
"license": "Apache",

View File

@@ -157,10 +157,11 @@ export class UnifiProtect extends ScryptedDeviceBase implements Settings, Device
const payload = updatePacket.payload as ProtectNvrUpdatePayloadEventAdd;
if (!payload.camera)
return;
const unifiCamera = this.cameras.get(payload.camera);
const nativeId = this.getNativeId({ id: payload.camera }, false);
const unifiCamera = this.cameras.get(nativeId);
if (!unifiCamera) {
this.console.log('unknown device event, sync needed?', payload.camera);
this.console.log('unknown device event, sync needed?', payload);
return;
}
@@ -195,7 +196,7 @@ export class UnifiProtect extends ScryptedDeviceBase implements Settings, Device
// id: '661d86bf03e69c03e408d62a',
// modelKey: 'event'
// }
if (payload.type === 'smartDetectZone' || payload.type === 'smartDetectLine') {
unifiCamera.resetDetectionTimeout();
@@ -602,7 +603,7 @@ export class UnifiProtect extends ScryptedDeviceBase implements Settings, Device
return this.storageSettings.values.idMaps.nativeId?.[nativeId] || nativeId;
}
getNativeId(device: any, update: boolean) {
getNativeId(device: { id?: string, mac?: string; anonymousDeviceId?: string }, update: boolean) {
const { id, mac, anonymousDeviceId } = device;
const idMaps = this.storageSettings.values.idMaps;

159
server/package-lock.json generated
View File

@@ -1,20 +1,20 @@
{
"name": "@scrypted/server",
"version": "0.106.0",
"version": "0.110.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@scrypted/server",
"version": "0.106.0",
"version": "0.110.4",
"hasInstallScript": true,
"license": "ISC",
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.11",
"@scrypted/ffmpeg-static": "^6.1.0-build1",
"@scrypted/node-pty": "^1.0.11",
"@scrypted/node-pty": "^1.0.14",
"@scrypted/types": "^0.3.30",
"adm-zip": "^0.5.12",
"adm-zip": "^0.5.14",
"body-parser": "^1.20.2",
"cookie-parser": "^1.4.6",
"dotenv": "^16.4.5",
@@ -29,13 +29,13 @@
"node-dijkstra": "^2.5.0",
"node-forge": "^1.3.1",
"node-gyp": "^10.1.0",
"py": "npm:@bjia56/portable-python@^0.1.47",
"py": "npm:@bjia56/portable-python@^0.1.51",
"router": "^1.3.8",
"semver": "^7.6.2",
"sharp": "^0.33.4",
"source-map-support": "^0.5.21",
"tar": "^7.2.0",
"tslib": "^2.6.2",
"tslib": "^2.6.3",
"typescript": "^5.4.5",
"whatwg-mimetype": "^4.0.0",
"ws": "^8.17.0"
@@ -702,37 +702,12 @@
}
},
"node_modules/@scrypted/node-pty": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@scrypted/node-pty/-/node-pty-1.0.11.tgz",
"integrity": "sha512-zCw8cFAz0Pd5P42sLeMpSyIO/n93OUjTpEajdd547rXDaZr1tWYF1D7LutO5+FiGI24w6Blplb2Sq1Tw7AWzmA==",
"version": "1.0.14",
"resolved": "https://registry.npmjs.org/@scrypted/node-pty/-/node-pty-1.0.14.tgz",
"integrity": "sha512-jqWXmvEgK1TH9GDod2mLYcL97sC5mw4+YdMz0EzCvdz4QyILHNTVyB98cfWdfPz33mZC0YOASh9BFrGk3cBzaA==",
"hasInstallScript": true,
"dependencies": {
"@scrypted/prebuild-install": "^7.1.2"
}
},
"node_modules/@scrypted/prebuild-install": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@scrypted/prebuild-install/-/prebuild-install-7.1.3.tgz",
"integrity": "sha512-scL9zHWbAcenhyx8fAv2U+mxrIWCL22VqL1ENOZpCQ7ByWOQERvOpgS4qlSNgXuuDfu+XBoyRvLMZcWIOSI74w==",
"dependencies": {
"detect-libc": "^2.0.0",
"expand-template": "^2.0.3",
"github-from-package": "0.0.0",
"minimist": "^1.2.3",
"mkdirp-classic": "^0.5.3",
"napi-build-utils": "^1.0.1",
"node-abi": "^3.3.0",
"pump": "^3.0.0",
"rc": "^1.2.7",
"simple-get": "^4.0.0",
"tar-fs": "^2.0.0",
"tunnel-agent": "^0.6.0"
},
"bin": {
"prebuild-install": "bin.js"
},
"engines": {
"node": ">=10"
"prebuild-install": "npm:@scrypted/prebuild-install@^7.1.8"
}
},
"node_modules/@scrypted/types": {
@@ -976,11 +951,11 @@
}
},
"node_modules/adm-zip": {
"version": "0.5.12",
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.12.tgz",
"integrity": "sha512-6TVU49mK6KZb4qG6xWaaM4C7sA/sgUMLy/JYMOzkcp3BvVLpW0fXDFQiIzAuxFCt/2+xD7fNIiPFAoLZPhVNLQ==",
"version": "0.5.14",
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.14.tgz",
"integrity": "sha512-DnyqqifT4Jrcvb8USYjp6FHtBpEIz1mnXu6pTRHZ0RL69LbQYiO+0lDFg5+OKA7U29oWSs3a/i8fhn8ZcceIWg==",
"engines": {
"node": ">=6.0"
"node": ">=12.0"
}
},
"node_modules/agent-base": {
@@ -1580,20 +1555,6 @@
}
}
},
"node_modules/decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"dependencies": {
"mimic-response": "^3.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
@@ -2417,17 +2378,6 @@
"node": ">= 0.6"
}
},
"node_modules/mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -2939,6 +2889,32 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
},
"node_modules/prebuild-install": {
"name": "@scrypted/prebuild-install",
"version": "7.1.8",
"resolved": "https://registry.npmjs.org/@scrypted/prebuild-install/-/prebuild-install-7.1.8.tgz",
"integrity": "sha512-ghmvo7gRUUyTUtjBGEcZfL7fizBNdzFFLnMRiNh3+lL25t8Rjv4EDBEKmDT6C4/wKVfdtzAnITBBc9r9DhVqRg==",
"dependencies": {
"detect-libc": "^2.0.0",
"expand-template": "^2.0.3",
"follow-redirects": "^1.15.6",
"github-from-package": "0.0.0",
"minimist": "^1.2.3",
"mkdirp-classic": "^0.5.3",
"napi-build-utils": "^1.0.1",
"node-abi": "^3.3.0",
"pump": "^3.0.0",
"rc": "^1.2.7",
"tar-fs": "^2.0.0",
"tunnel-agent": "^0.6.0"
},
"bin": {
"prebuild-install": "bin.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/proc-log": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz",
@@ -2982,9 +2958,9 @@
},
"node_modules/py": {
"name": "@bjia56/portable-python",
"version": "0.1.47",
"resolved": "https://registry.npmjs.org/@bjia56/portable-python/-/portable-python-0.1.47.tgz",
"integrity": "sha512-NU7ryvrWLWueRL/3sMBvYOAmuh/zKzHzZkcABK3CCxbrVrHMcmkCo1fta4sEP++NPb/29rEG9oWF/vdyMoOQlg==",
"version": "0.1.51",
"resolved": "https://registry.npmjs.org/@bjia56/portable-python/-/portable-python-0.1.51.tgz",
"integrity": "sha512-Ug+TBsoD+H0/jknw0lZ0oxGEPwQPCk5B6TEyX24QM2mX9w1FHhD0oNbhW3biRw31KjWK0uyJux01tcaRUpUtRQ==",
"dependencies": {
"adm-zip": "^0.5.10"
}
@@ -3337,49 +3313,6 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
},
"node_modules/simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/simple-get": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"decompress-response": "^6.0.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"node_modules/simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
@@ -3677,9 +3610,9 @@
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
},
"node_modules/tunnel-agent": {
"version": "0.6.0",

View File

@@ -1,13 +1,13 @@
{
"name": "@scrypted/server",
"version": "0.107.0",
"version": "0.110.5",
"description": "",
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.11",
"@scrypted/ffmpeg-static": "^6.1.0-build1",
"@scrypted/node-pty": "^1.0.11",
"@scrypted/node-pty": "^1.0.14",
"@scrypted/types": "^0.3.30",
"adm-zip": "^0.5.12",
"adm-zip": "^0.5.14",
"body-parser": "^1.20.2",
"cookie-parser": "^1.4.6",
"dotenv": "^16.4.5",
@@ -22,13 +22,13 @@
"node-dijkstra": "^2.5.0",
"node-forge": "^1.3.1",
"node-gyp": "^10.1.0",
"py": "npm:@bjia56/portable-python@^0.1.47",
"py": "npm:@bjia56/portable-python@^0.1.51",
"router": "^1.3.8",
"semver": "^7.6.2",
"sharp": "^0.33.4",
"source-map-support": "^0.5.21",
"tar": "^7.2.0",
"tslib": "^2.6.2",
"tslib": "^2.6.3",
"typescript": "^5.4.5",
"whatwg-mimetype": "^4.0.0",
"ws": "^8.17.0"

View File

@@ -41,7 +41,7 @@ const StreamParser: FetchParser<IncomingMessage> = {
}
}
export function getHttpFetchParser(responseType: HttpFetchResponseType) {
export function getHttpFetchParser(responseType: HttpFetchResponseType | undefined) {
switch (responseType) {
case 'json':
return JSONParser;
@@ -53,7 +53,7 @@ export function getHttpFetchParser(responseType: HttpFetchResponseType) {
return BufferParser;
}
export function httpFetchParseIncomingMessage(readable: IncomingMessage, responseType: HttpFetchResponseType) {
export function httpFetchParseIncomingMessage(readable: IncomingMessage, responseType: HttpFetchResponseType | undefined) {
return getHttpFetchParser(responseType).parse(readable);
}

View File

@@ -62,7 +62,7 @@ export type fetcher<B, M> = <T extends HttpFetchOptions<B>>(options: T) => Promi
>>;
export function getHttpFetchAccept(responseType: HttpFetchResponseType) {
export function getHttpFetchAccept(responseType: HttpFetchResponseType | undefined) {
switch (responseType) {
case 'json':
return 'application/json';
@@ -89,7 +89,7 @@ export function setHeader(headers: [string, string][], key: string, value: strin
headers.push([key, value]);
}
export function setDefaultHttpFetchAccept(headers: [string, string][], responseType: HttpFetchResponseType) {
export function setDefaultHttpFetchAccept(headers: [string, string][], responseType: HttpFetchResponseType | undefined) {
if (hasHeader(headers, 'Accept'))
return;
const accept = getHttpFetchAccept(responseType);
@@ -97,7 +97,7 @@ export function setDefaultHttpFetchAccept(headers: [string, string][], responseT
setHeader(headers, 'Accept', accept);
}
export function createHeadersArray(headers: HeadersInit): [string, string][] {
export function createHeadersArray(headers: HeadersInit | undefined): [string, string][] {
const headersArray: [string, string][] = [];
if (!headers)
return headersArray;
@@ -144,7 +144,7 @@ export function createStringOrBufferBody(headers: [string, string][], body: any)
return body;
}
export async function domFetchParseIncomingMessage(response: Response, responseType: HttpFetchResponseType) {
export async function domFetchParseIncomingMessage(response: Response, responseType: HttpFetchResponseType | undefined) {
switch (responseType) {
case 'json':
return response.json();

View File

@@ -13,7 +13,7 @@ export async function listenZero(server: net.Server, hostname?: string) {
return (server.address() as net.AddressInfo).port;
}
export async function listenZeroSingleClient(hostname?: string, options?: net.ServerOpts) {
export async function listenZeroSingleClient(hostname?: string, options?: net.ServerOpts, listenTimeout = 30000) {
const server = new net.Server(options);
const port = await listenZero(server, hostname);
@@ -22,7 +22,7 @@ export async function listenZeroSingleClient(hostname?: string, options?: net.Se
const timeout = setTimeout(() => {
server.close();
reject(new ListenZeroSingleClientTimeoutError());
}, 30000);
}, listenTimeout);
cancel = () => {
clearTimeout(timeout);
server.close();

View File

@@ -28,6 +28,12 @@ import { RuntimeWorker } from './runtime/runtime-worker';
const serverVersion = require('../../package.json').version;
export class UnsupportedRuntimeError extends Error {
constructor(runtime: string) {
super(`Unsupported runtime: ${runtime}`);
}
}
export class PluginHost {
worker: RuntimeWorker;
peer: RpcPeer;
@@ -281,7 +287,7 @@ export class PluginHost {
const workerHost = this.scrypted.pluginHosts.get(runtime);
if (!workerHost)
throw new Error(`Unsupported Scrypted runtime: ${this.packageJson.scrypted.runtime}`);
throw new UnsupportedRuntimeError(this.packageJson.scrypted.runtime);
this.worker = workerHost(this.scrypted.mainFilename, this.pluginId, {
packageJson: this.packageJson,

View File

@@ -12,7 +12,6 @@ import net from 'net';
import path from 'path';
import { ParsedQs } from 'qs';
import semver from 'semver';
import { PassThrough } from 'stream';
import { Parser as TarParser } from 'tar';
import { URL } from "url";
import WebSocket, { Server as WebSocketServer } from "ws";
@@ -29,10 +28,11 @@ import { getMixins, hasMixinCycle } from './mixin/mixin-cycle';
import { AccessControls } from './plugin/acl';
import { PluginDebug } from './plugin/plugin-debug';
import { PluginDeviceProxyHandler } from './plugin/plugin-device';
import { PluginHost } from './plugin/plugin-host';
import { PluginHost, UnsupportedRuntimeError } from './plugin/plugin-host';
import { isConnectionUpgrade, PluginHttp } from './plugin/plugin-http';
import { WebSocketConnection } from './plugin/plugin-remote-websocket';
import { getPluginVolume } from './plugin/plugin-volume';
import { CustomRuntimeWorker } from './plugin/runtime/custom-worker';
import { NodeForkWorker } from './plugin/runtime/node-fork-worker';
import { PythonRuntimeWorker } from './plugin/runtime/python-worker';
import { RuntimeWorker, RuntimeWorkerOptions } from './plugin/runtime/runtime-worker';
@@ -46,7 +46,6 @@ import { getNpmPackageInfo, PluginComponent } from './services/plugin';
import { ServiceControl } from './services/service-control';
import { UsersService } from './services/users';
import { getState, ScryptedStateManager, setState } from './state';
import { CustomRuntimeWorker } from './plugin/runtime/custom-worker';
interface DeviceProxyPair {
handler: PluginDeviceProxyHandler;
@@ -630,8 +629,8 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
return this.runPlugin(plugin, pluginDebug);
}
setupPluginHostAutoRestart(pluginHost: PluginHost) {
const logger = this.getDeviceLogger(this.findPluginDevice(pluginHost.pluginId));
setupPluginHostAutoRestart(pluginId: string, pluginHost?: PluginHost) {
const logger = this.getDeviceLogger(this.findPluginDevice(pluginId));
let timeout: NodeJS.Timeout;
@@ -640,20 +639,20 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
return;
const t = 60000;
pluginHost.kill();
logger.log('e', `plugin ${pluginHost.pluginId} unexpectedly exited, restarting in ${t}ms`);
pluginHost?.kill();
logger.log('e', `plugin ${pluginId} unexpectedly exited, restarting in ${t}ms`);
timeout = setTimeout(async () => {
timeout = undefined;
const plugin = await this.datastore.tryGet(Plugin, pluginHost.pluginId);
const plugin = await this.datastore.tryGet(Plugin, pluginId);
if (!plugin) {
logger.log('w', `scheduled plugin restart cancelled, plugin no longer exists ${pluginHost.pluginId}`);
logger.log('w', `scheduled plugin restart cancelled, plugin no longer exists ${pluginId}`);
return;
}
const existing = this.plugins[pluginHost.pluginId];
if (existing && existing !== pluginHost && !existing.killed) {
logger.log('w', `scheduled plugin restart cancelled, plugin was restarted by user ${pluginHost.pluginId}`);
const existing = this.plugins[pluginId];
if (existing && pluginHost && existing !== pluginHost && !existing.killed) {
logger.log('w', `scheduled plugin restart cancelled, plugin was restarted by user ${pluginId}`);
return;
}
@@ -661,32 +660,52 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
this.runPlugin(plugin);
}
catch (e) {
logger.log('e', `error restarting plugin ${pluginHost.pluginId}`);
logger.log('e', `error restarting plugin ${pluginId}`);
logger.log('e', e.toString());
restart();
}
}, t);
};
pluginHost.worker.once('error', restart);
pluginHost.worker.once('exit', restart);
pluginHost.worker.once('close', restart);
1
if (pluginHost) {
pluginHost.worker.once('error', restart);
pluginHost.worker.once('exit', restart);
pluginHost.worker.once('close', restart);
}
else {
restart();
}
}
loadPlugin(plugin: Plugin, pluginDebug?: PluginDebug) {
const pluginId = plugin._id;
this.killPlugin(pluginId);
try {
this.killPlugin(pluginId);
const pluginDevices = this.findPluginDevices(pluginId);
for (const pluginDevice of pluginDevices) {
this.invalidatePluginDevice(pluginDevice._id);
const pluginDevices = this.findPluginDevices(pluginId);
for (const pluginDevice of pluginDevices) {
this.invalidatePluginDevice(pluginDevice._id);
}
const pluginHost = new PluginHost(this, plugin, pluginDebug);
this.plugins[pluginId] = pluginHost;
this.setupPluginHostAutoRestart(pluginId, pluginHost);
return pluginHost;
}
catch (e) {
const logger = this.getDeviceLogger(this.findPluginDevice(pluginId));
if (e instanceof UnsupportedRuntimeError) {
logger.log('e', 'error loading plugin (not retrying)');
logger.log('e', e.toString());
throw e;
}
const pluginHost = new PluginHost(this, plugin, pluginDebug);
this.setupPluginHostAutoRestart(pluginHost);
this.plugins[pluginId] = pluginHost;
return pluginHost;
logger.log('e', 'error loading plugin (retrying...)');
logger.log('e', e.toString());
this.setupPluginHostAutoRestart(pluginId);
throw e;
}
}
probePluginDevices(plugin: Plugin) {