diff --git a/plugins/opencv/.gitignore b/plugins/opencv/.gitignore index 72e8ffc0d..9cdb546bf 100644 --- a/plugins/opencv/.gitignore +++ b/plugins/opencv/.gitignore @@ -1 +1,4 @@ -* +.DS_Store +out/ +node_modules/ +dist/ diff --git a/plugins/opencv/package-lock.json b/plugins/opencv/package-lock.json index fc42b7a55..9b5ab7d40 100644 --- a/plugins/opencv/package-lock.json +++ b/plugins/opencv/package-lock.json @@ -1,20 +1,22 @@ { "name": "@scrypted/opencv", - "version": "0.0.17", + "version": "0.0.12", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/opencv", - "version": "0.0.17", + "version": "0.0.12", "license": "Apache-2.0", "dependencies": { "@scrypted/common": "file:../../common", - "@scrypted/sdk": "file:../../sdk", - "opencv-wasm": "^4.3.0-10" + "@scrypted/sdk": "file:../../sdk" }, "devDependencies": { "@types/node": "^14.17.11" + }, + "optionalDependencies": { + "@koush/opencv4nodejs": "^5.6.6" } }, "../../common": { @@ -47,6 +49,7 @@ "babel-loader": "^8.2.3", "babel-plugin-const-enum": "^1.1.0", "esbuild": "^0.13.8", + "ncp": "^2.0.0", "raw-loader": "^4.0.2", "rimraf": "^3.0.2", "ts-loader": "^9.2.6", @@ -65,6 +68,21 @@ "../sdk": { "extraneous": true }, + "node_modules/@koush/opencv4nodejs": { + "version": "5.6.6", + "resolved": "https://registry.npmjs.org/@koush/opencv4nodejs/-/opencv4nodejs-5.6.6.tgz", + "integrity": "sha512-PHwfe+j4+UmgMoh70aM/lNnFxb+f/okLSZRKE7Aq+cqDyS8XOBn5cFRcTEbTpVjJslGV8ab7o+jDKQ0ukGJDxw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "nan": "^2.14.0", + "native-node-utils": "^0.2.7", + "npmlog": "^4.1.2" + }, + "optionalDependencies": { + "@types/node": ">6" + } + }, "node_modules/@scrypted/common": { "resolved": "../../common", "link": true @@ -77,15 +95,254 @@ "version": "14.17.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.11.tgz", "integrity": "sha512-n2OQ+0Bz6WEsUjrvcHD1xZ8K+Kgo4cn9/w94s1bJS690QMUWfJPW/m7CCb7gPkA1fcYwL2UpjXP/rq/Eo41m6w==", - "dev": true + "devOptional": true }, - "node_modules/opencv-wasm": { - "version": "4.3.0-10", - "resolved": "https://registry.npmjs.org/opencv-wasm/-/opencv-wasm-4.3.0-10.tgz", - "integrity": "sha512-EWmWLUzp2suoc6N44Y4ouWT85QwvShx23Q430R+lp6NyS828bjQn6mCgA3NJ6Z/S59aaTeeu+RhqPQIJIYld1w==" + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "optional": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "optional": true + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "optional": true + }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "optional": true, + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "optional": true + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "optional": true + }, + "node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "optional": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "optional": true + }, + "node_modules/nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "optional": true + }, + "node_modules/native-node-utils": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/native-node-utils/-/native-node-utils-0.2.7.tgz", + "integrity": "sha512-61v0G3uVxWlXHppSZGwZi+ZEIgGUKI8QvEkEJLb1GVePI7P8SBe+G747z+QMXSt4TxfgbVZP0DyobbRKYVIjdw==", + "optional": true, + "dependencies": { + "nan": "^2.13.2" + } + }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "optional": true, + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "optional": true + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "optional": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "optional": true + }, + "node_modules/signal-exit": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "optional": true + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "optional": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "optional": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "optional": true + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } } }, "dependencies": { + "@koush/opencv4nodejs": { + "version": "5.6.6", + "resolved": "https://registry.npmjs.org/@koush/opencv4nodejs/-/opencv4nodejs-5.6.6.tgz", + "integrity": "sha512-PHwfe+j4+UmgMoh70aM/lNnFxb+f/okLSZRKE7Aq+cqDyS8XOBn5cFRcTEbTpVjJslGV8ab7o+jDKQ0ukGJDxw==", + "optional": true, + "requires": { + "@types/node": ">6", + "nan": "^2.14.0", + "native-node-utils": "^0.2.7", + "npmlog": "^4.1.2" + } + }, "@scrypted/common": { "version": "file:../../common", "requires": { @@ -110,6 +367,7 @@ "babel-loader": "^8.2.3", "babel-plugin-const-enum": "^1.1.0", "esbuild": "^0.13.8", + "ncp": "^2.0.0", "raw-loader": "^4.0.2", "rimraf": "^3.0.2", "ts-loader": "^9.2.6", @@ -121,12 +379,218 @@ "version": "14.17.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.11.tgz", "integrity": "sha512-n2OQ+0Bz6WEsUjrvcHD1xZ8K+Kgo4cn9/w94s1bJS690QMUWfJPW/m7CCb7gPkA1fcYwL2UpjXP/rq/Eo41m6w==", - "dev": true + "devOptional": true }, - "opencv-wasm": { - "version": "4.3.0-10", - "resolved": "https://registry.npmjs.org/opencv-wasm/-/opencv-wasm-4.3.0-10.tgz", - "integrity": "sha512-EWmWLUzp2suoc6N44Y4ouWT85QwvShx23Q430R+lp6NyS828bjQn6mCgA3NJ6Z/S59aaTeeu+RhqPQIJIYld1w==" + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "optional": true + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "optional": true + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "optional": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "optional": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "optional": true + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "optional": true + }, + "nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "optional": true + }, + "native-node-utils": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/native-node-utils/-/native-node-utils-0.2.7.tgz", + "integrity": "sha512-61v0G3uVxWlXHppSZGwZi+ZEIgGUKI8QvEkEJLb1GVePI7P8SBe+G747z+QMXSt4TxfgbVZP0DyobbRKYVIjdw==", + "optional": true, + "requires": { + "nan": "^2.13.2" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "optional": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "optional": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "optional": true + }, + "signal-exit": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "optional": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } } } } diff --git a/plugins/opencv/package.json b/plugins/opencv/package.json index 88f900081..ee3adb4e6 100644 --- a/plugins/opencv/package.json +++ b/plugins/opencv/package.json @@ -5,16 +5,15 @@ "author": "Scrypted", "license": "Apache-2.0", "scripts": { - "prepublishOnly": "npm run opencv-deps && NODE_ENV=production scrypted-webpack", - "prescrypted-vscode-launch": "npm run opencv-deps && scrypted-webpack", + "prepublishOnly": "NODE_ENV=production scrypted-webpack", + "prescrypted-vscode-launch": "scrypted-webpack", "scrypted-vscode-launch": "scrypted-deploy-debug", "scrypted-deploy-debug": "scrypted-deploy-debug", "scrypted-debug": "scrypted-debug", "scrypted-deploy": "scrypted-deploy", "scrypted-readme": "scrypted-readme", "scrypted-package-json": "scrypted-package-json", - "opencv-deps": "cp node_modules/opencv-wasm/index.js src/opencv && cp node_modules/opencv-wasm/opencv.js src/opencv && sed -i '' 's/this, function/exports, function/g' src/opencv/opencv.js", - "scrypted-webpack": "npm run opencv-deps && scrypted-webpack" + "scrypted-webpack": "scrypted-webpack" }, "keywords": [ "scrypted", @@ -31,12 +30,15 @@ "type": "API", "interfaces": [ "MixinProvider" - ] + ], + "realfs": true }, "dependencies": { "@scrypted/common": "file:../../common", - "@scrypted/sdk": "file:../../sdk", - "opencv-wasm": "^4.3.0-10" + "@scrypted/sdk": "file:../../sdk" + }, + "optionalDependencies": { + "@koush/opencv4nodejs": "^5.6.6" }, "devDependencies": { "@types/node": "^14.17.11" diff --git a/plugins/opencv/src/main.ts b/plugins/opencv/src/main.ts index 9a4329787..9ec27f424 100644 --- a/plugins/opencv/src/main.ts +++ b/plugins/opencv/src/main.ts @@ -1,3 +1,4 @@ + import { MixinProvider, ScryptedDeviceType, ScryptedInterface, VideoCamera, MediaStreamOptions, Settings, Setting, ScryptedMimeTypes, FFMpegInput, MotionSensor } from '@scrypted/sdk'; import sdk from '@scrypted/sdk'; import { once } from 'events'; @@ -5,15 +6,7 @@ import { SettingsMixinDeviceBase } from "../../../common/src/settings-mixin"; import { FFMpegRebroadcastSession, startRebroadcastSession } from '@scrypted/common/src/ffmpeg-rebroadcast'; import { StreamChunk, createRawVideoParser } from '@scrypted/common/src/stream-parser'; import { AutoenableMixinProvider } from '@scrypted/common/src/autoenable-mixin-provider'; -import { getH264DecoderArgs } from "@scrypted/common/src/ffmpeg-hardware-acceleration" -import { HeapScope } from './heap'; - -// necessary for opencv wasm to not crap itself due to webpack. -global.__filename = undefined; -// todo: remove this. -window = undefined; -// import { cv } from 'opencv-wasm'; -const { cv } = require('./opencv'); +import cv, { Mat, Size } from "@koush/opencv4nodejs"; const { mediaManager, log, systemManager, deviceManager } = sdk; @@ -104,11 +97,6 @@ class OpenCVMixin extends SettingsMixinDeviceBase implements Motion width = Math.floor(width / 6) * 6; height = Math.floor(height / 6) * 6; - const videoDecoderArguments = this.storage.getItem('videoDecoderArguments') || ''; - if (videoDecoderArguments) { - ffmpegInput.inputArguments.unshift(...videoDecoderArguments.split(' ')); - } - this.sessionPromise = startRebroadcastSession(ffmpegInput, { console: this.console, parsers: { @@ -123,7 +111,6 @@ class OpenCVMixin extends SettingsMixinDeviceBase implements Motion }); const session = await this.sessionPromise; - session.events.on('error', e => this.console.log('ffmpeg error', e)); let watchdog: NodeJS.Timeout; const restartWatchdog = () => { @@ -145,88 +132,57 @@ class OpenCVMixin extends SettingsMixinDeviceBase implements Motion await this.startWrapped(session); } finally { - clearTimeout(watchdog); session.kill(); } } async startWrapped(session: FFMpegRebroadcastSession) { - let previousFrame: any; - try { - let timeout: NodeJS.Timeout; + let previousFrame: Mat; + let timeout: NodeJS.Timeout; - const triggerMotion = () => { - this.motionDetected = true; - clearTimeout(timeout); - setTimeout(() => this.motionDetected = false, 10000); + const triggerMotion = () => { + this.motionDetected = true; + clearTimeout(timeout); + setTimeout(() => this.motionDetected = false, 10000); + } + + this.motionDetected = false; + + while (!this.released) { + if (this.motionDetected) { + // during motion just eat the frames. + previousFrame = undefined; + await sleep(1000); + continue; } - this.motionDetected = false; + const args = await once(session.events, 'rawvideo-data'); + const chunk: StreamChunk = args[0]; + // should be one chunk from the parser, but let's not assume that. + const raw = chunk.chunks.length === 1 ? chunk.chunks[0] : Buffer.concat(chunk.chunks); + const mat = new Mat(raw, chunk.height * 3 / 2, chunk.width, cv.CV_8U); - while (!this.released && session.isActive()) { - if (this.motionDetected) { - // during motion just eat the frames. - previousFrame = undefined; - await sleep(1000); + const gray = await mat.cvtColorAsync(cv.COLOR_YUV420p2GRAY); + const curFrame = await gray.gaussianBlurAsync(new Size(21, 21), 0); + + try { + if (!previousFrame) { continue; } - const args = await once(session.events, 'rawvideo-data'); - const chunk: StreamChunk = args[0]; - // should be one chunk from the parser, but let's not assume that. - const raw = chunk.chunks.length === 1 ? chunk.chunks[0] : Buffer.concat(chunk.chunks); - - const scope = new HeapScope(); - const mat = scope.new(cv.Mat, chunk.height * 3 / 2, chunk.width, cv.CV_8U); - mat.data.set(raw); - - const gray = scope.new(cv.Mat); - cv.cvtColor(mat, gray, cv.COLOR_YUV420p2GRAY); - const curFrame = new cv.Mat(); - cv.GaussianBlur(gray, curFrame, new cv.Size(21, 21), 0); - - try { - if (!previousFrame) { - continue; - } - - const frameDelta = scope.new(cv.Mat); - cv.absdiff(previousFrame, curFrame, frameDelta); - const thresh = scope.new(cv.Mat); - cv.threshold(frameDelta, thresh, this.threshold, 255, cv.THRESH_BINARY); - const dilated = scope.new(cv.Mat); - const structuringElement = cv.getStructuringElement(cv.MORPH_ELLIPSE, new cv.Size(4, 4)); - scope.tracking.push(structuringElement); - cv.dilate(thresh, dilated, structuringElement, new cv.Point(-1, -1), 2); - const contours = scope.new(cv.MatVector); - const hierarchy = scope.new(cv.Mat); - cv.findContours(dilated, contours, hierarchy, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); - const filteredContours: number[] = []; - for (let i = 0; i < contours.size(); i++) { - const contourArea = cv.contourArea(contours.get(i)); - if (contourArea > this.area) { - filteredContours.push(contourArea); - } - } - if (filteredContours.length) { - this.console.log('motion triggered by area(s)', filteredContours.join(',')); - triggerMotion(); - } - } - catch (e) { - this.console.log('cv error', e); - throw e; - } - finally { - previousFrame?.delete(); - previousFrame = curFrame; - - scope.dispose(); + const frameDelta = previousFrame.absdiff(curFrame); + const thresh = await frameDelta.thresholdAsync(this.threshold, 255, cv.THRESH_BINARY); + const dilated = await thresh.dilateAsync(cv.getStructuringElement(cv.MORPH_ELLIPSE, new cv.Size(4, 4)), new cv.Point2(-1, -1), 2) + const contours = await dilated.findContoursAsync(cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); + const filteredContours = contours.filter(cnt => cnt.area > this.area).map(cnt => cnt.area); + if (filteredContours.length) { + this.console.log('motion triggered by area(s)', filteredContours.join(',')); + triggerMotion(); } } - } - finally { - previousFrame?.delete(); + finally { + previousFrame = curFrame; + } } } @@ -252,18 +208,7 @@ class OpenCVMixin extends SettingsMixinDeviceBase implements Motion }); } - const decoderArgs = getH264DecoderArgs(); - settings.push( - { - title: 'Video Decoder Arguments', - key: "videoDecoderArguments", - value: this.storage.getItem('videoDecoderArguments'), - description: 'FFmpeg arguments used to decode input video.', - placeholder: '-hwaccel auto', - choices: Object.keys(decoderArgs), - combobox: true, - }, { title: "Motion Area", description: "The area size required to trigger motion. Higher values (larger areas) are less sensitive.", @@ -294,26 +239,19 @@ class OpenCVMixin extends SettingsMixinDeviceBase implements Motion } async putMixinSetting(key: string, value: string | number | boolean): Promise { + this.storage.setItem(key, value.toString()); if (key === 'area') this.area = parseInt(value.toString()) || defaultArea; if (key === 'threshold') this.threshold = parseInt(value.toString()) || defaultThreshold; - if (key === 'videoDecoderArguments') { - const decoderArgs = getH264DecoderArgs(); - value = decoderArgs[value.toString()]?.join(' ') || value; - } - if (key === 'motionChannel' || key === 'videoDecoderArguments') { + if (key === 'motionChannel') { this.sessionPromise?.then(session => session.kill()); } - - this.storage.setItem(key, value.toString()); - deviceManager.onMixinEvent(this.id, this.mixinProviderNativeId, ScryptedInterface.Settings, undefined); } release() { this.released = true; - this.sessionPromise?.then(session => session.kill()); } } diff --git a/sdk/bin/scrypted-webpack.js b/sdk/bin/scrypted-webpack.js index 239e43a11..6eec4c1b0 100755 --- a/sdk/bin/scrypted-webpack.js +++ b/sdk/bin/scrypted-webpack.js @@ -17,6 +17,7 @@ const rimraf = require('rimraf'); const webpack = require('webpack'); const esbuild = require('esbuild'); const ncp = require('ncp'); +const tmp = require('tmp'); if (fs.existsSync(path.resolve(cwd, 'src/main.py'))) { let out; @@ -91,6 +92,9 @@ else { } } + const packageJson = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json').toString())); + const optionalDependencies = Object.keys(packageJson.optionalDependencies || {}); + const runtimes = [ // { // config: 'webpack.duktape.config.js', @@ -158,6 +162,19 @@ else { config.output.path = out; config.output.filename = runtime.output; + + for (const opt of optionalDependencies) { + const t = tmp.tmpNameSync({ + postfix: '.js', + }); + console.log(t); + fs.writeFileSync(t, ` + const e = __non_webpack_require__('${opt}'); + module.exports = e; + `); + config.resolve.alias[opt] = t; + } + webpack(config, (err, stats) => { if (err) return reject(err); diff --git a/sdk/package-lock.json b/sdk/package-lock.json index 8fbc4a0f7..d4db8ba5f 100644 --- a/sdk/package-lock.json +++ b/sdk/package-lock.json @@ -25,6 +25,7 @@ "ncp": "^2.0.0", "raw-loader": "^4.0.2", "rimraf": "^3.0.2", + "tmp": "^0.2.1", "ts-loader": "^9.2.6", "typescript-json-schema": "^0.50.1", "webpack": "^5.59.0" @@ -2202,6 +2203,17 @@ "node": ">= 8" } }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -4141,6 +4153,14 @@ } } }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "requires": { + "rimraf": "^3.0.0" + } + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", diff --git a/sdk/package.json b/sdk/package.json index 71ff02cb5..62b5dd400 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -35,6 +35,7 @@ "ncp": "^2.0.0", "raw-loader": "^4.0.2", "rimraf": "^3.0.2", + "tmp": "^0.2.1", "ts-loader": "^9.2.6", "typescript-json-schema": "^0.50.1", "webpack": "^5.59.0" diff --git a/server/src/plugin/plugin-host.ts b/server/src/plugin/plugin-host.ts index 88875df80..1477f118c 100644 --- a/server/src/plugin/plugin-host.ts +++ b/server/src/plugin/plugin-host.ts @@ -27,6 +27,17 @@ import { PluginDebug } from './plugin-debug'; import readline from 'readline'; import { Readable, Writable } from 'stream'; +export function ensurePluginVolume(pluginId: string) { + const volume = path.join(process.cwd(), 'volume'); + const pluginVolume = path.join(volume, 'plugins', pluginId); + try { + mkdirp.sync(pluginVolume); + } + catch (e) { + } + return pluginVolume; +} + export class PluginHost { worker: child_process.ChildProcess; peer: RpcPeer; @@ -91,14 +102,10 @@ export class PluginHost { const logger = scrypted.getDeviceLogger(scrypted.findPluginDevice(plugin._id)); const volume = path.join(process.cwd(), 'volume'); - const cwd = path.join(volume, 'plugins', this.pluginId); - try { - mkdirp.sync(cwd); - } - catch (e) { - } + const cwd = ensurePluginVolume(this.pluginId); this.startPluginClusterHost(logger, { + NODE_PATH: path.join(cwd, 'node_modules'), SCRYPTED_PLUGIN_VOLUME: cwd, }, plugin.packageJson.scrypted.runtime); diff --git a/server/src/runtime.ts b/server/src/runtime.ts index 223fbd8f6..7dc827b13 100644 --- a/server/src/runtime.ts +++ b/server/src/runtime.ts @@ -1,5 +1,5 @@ import { Level } from './level'; -import { PluginHost } from './plugin/plugin-host'; +import { ensurePluginVolume, PluginHost } from './plugin/plugin-host'; import cluster from 'cluster'; import { ScryptedNativeId, Device, EngineIOHandler, HttpRequest, HttpRequestHandler, OauthClient, PushHandler, ScryptedDevice, ScryptedInterface, ScryptedInterfaceProperty } from '@scrypted/sdk/types'; import { PluginDeviceProxyHandler } from './plugin/plugin-device'; @@ -29,6 +29,9 @@ import { Alerts } from './services/alerts'; import { Info } from './services/info'; import io from 'engine.io'; import {spawn as ptySpawn} from 'node-pty'; +import child_process from 'child_process'; +import fs from 'fs'; +import path from 'path'; interface DeviceProxyPair { handler: PluginDeviceProxyHandler; @@ -424,6 +427,31 @@ export class ScryptedRuntime { return proxyPair; } + async installOptionalDependencies(packageJson: any, currentPackageJson: any) { + const { optionalDependencies } = packageJson; + if (!optionalDependencies) + return; + if (!Object.keys(packageJson).length) + return; + const currentOptionalDependencies = currentPackageJson?.optionalDependencies || {}; + + if (JSON.stringify(optionalDependencies) === JSON.stringify(currentOptionalDependencies)) + return; + + const reduced = Object.assign({}, packageJson); + delete reduced.dependencies; + delete reduced.devDependencies; + + const pluginVolume = ensurePluginVolume(reduced.name); + const optPj = path.join(pluginVolume, 'package.json'); + fs.writeFileSync(optPj, JSON.stringify(reduced)); + const cp = child_process.spawn('npm', ['install'], { + cwd: pluginVolume, + }); + + await once(cp, 'exit'); + } + async installNpm(pkg: string, version?: string): Promise { const registry = (await axios(`https://registry.npmjs.org/${pkg}`)).data; if (!version) { @@ -458,6 +486,7 @@ export class ScryptedRuntime { const packageJson = JSON.parse(packageJsonEntry.toString()); const npmPackage = packageJson.name; const plugin = await this.datastore.tryGet(Plugin, npmPackage) || new Plugin(); + await this.installOptionalDependencies(packageJson, plugin.packageJson); plugin._id = npmPackage; plugin.packageJson = packageJson; diff --git a/server/src/scrypted-main.ts b/server/src/scrypted-main.ts index 14eaba6a0..b52e53684 100644 --- a/server/src/scrypted-main.ts +++ b/server/src/scrypted-main.ts @@ -286,6 +286,9 @@ else { const npmPackage = req.query.npmPackage as string; const plugin = await db.tryGet(Plugin, npmPackage) || new Plugin(); + const packageJson = req.body; + await scrypted.installOptionalDependencies(packageJson, plugin.packageJson); + plugin._id = npmPackage; plugin.packageJson = req.body;