mirror of
https://github.com/koush/scrypted.git
synced 2026-02-05 23:22:13 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
10
.github/workflows/docker-common.yml
vendored
10
.github/workflows/docker-common.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
# runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
NODE_VERSION: ["18"]
|
||||
NODE_VERSION: ["18", "20"]
|
||||
BASE: ["jammy"]
|
||||
FLAVOR: ["full", "lite", "thin"]
|
||||
steps:
|
||||
@@ -23,13 +23,13 @@ jobs:
|
||||
- name: Set up SSH
|
||||
uses: MrSquaare/ssh-setup-action@v2
|
||||
with:
|
||||
host: Koushik-MacStudio
|
||||
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: raspberrypi
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM7 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
@@ -37,9 +37,9 @@ jobs:
|
||||
with:
|
||||
platforms: linux/arm64,linux/armhf
|
||||
append: |
|
||||
- endpoint: ssh://koush@Koushik-MacStudio
|
||||
- endpoint: ssh://${{ secrets.DOCKER_SSH_USER }}@${{ secrets.DOCKER_SSH_HOST_ARM64 }}
|
||||
platforms: linux/arm64
|
||||
- endpoint: ssh://koush@raspberrypi
|
||||
- endpoint: ssh://${{ secrets.DOCKER_SSH_USER }}@${{ secrets.DOCKER_SSH_HOST_ARM7 }}
|
||||
platforms: linux/armhf
|
||||
|
||||
- name: Login to Docker Hub
|
||||
|
||||
13
.github/workflows/docker.yml
vendored
13
.github/workflows/docker.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
# runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
BASE: ["18-jammy-full", "18-jammy-lite", "18-jammy-thin"]
|
||||
BASE: ["18-jammy-full", "18-jammy-lite", "18-jammy-thin", "20-jammy-full", "20-jammy-lite", "20-jammy-thin"]
|
||||
SUPERVISOR: ["", ".s6"]
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
@@ -42,13 +42,13 @@ jobs:
|
||||
- name: Set up SSH
|
||||
uses: MrSquaare/ssh-setup-action@v2
|
||||
with:
|
||||
host: Koushik-MacStudio
|
||||
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: raspberrypi
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM7 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
@@ -56,12 +56,11 @@ jobs:
|
||||
with:
|
||||
platforms: linux/arm64,linux/armhf
|
||||
append: |
|
||||
- endpoint: ssh://koush@Koushik-MacStudio
|
||||
# platforms: linux/arm64
|
||||
- endpoint: ssh://${{ secrets.DOCKER_SSH_USER }}@${{ secrets.DOCKER_SSH_HOST_ARM64 }}
|
||||
platforms: linux/arm64
|
||||
- endpoint: ssh://koush@raspberrypi
|
||||
- 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:
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
2
external/werift
vendored
2
external/werift
vendored
Submodule external/werift updated: 9815d03344...b63f339b55
@@ -1,6 +1,6 @@
|
||||
# Home Assistant Addon Configuration
|
||||
name: Scrypted
|
||||
version: "18-jammy-full.s6-v0.41.0"
|
||||
version: "18-jammy-full.s6-v0.50.0"
|
||||
slug: scrypted
|
||||
description: Scrypted is a high performance home video integration and automation platform
|
||||
url: "https://github.com/koush/scrypted"
|
||||
|
||||
@@ -50,7 +50,7 @@ services:
|
||||
# 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/sda/video:/nvr
|
||||
# - /mnt/media/video:/nvr
|
||||
|
||||
# Or use a network mount from one of the CIFS/NFS examples at the top of this file.
|
||||
# - type: volume
|
||||
|
||||
@@ -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 {
|
||||
@@ -155,8 +154,12 @@ export async function loginScryptedClient(options: ScryptedLoginOptions) {
|
||||
export async function checkScryptedClientLogin(options?: ScryptedConnectionOptions) {
|
||||
let { baseUrl } = options || {};
|
||||
const url = combineBaseUrl(baseUrl, 'login');
|
||||
const headers: AxiosRequestHeaders = {};
|
||||
if (options?.previousLoginResult?.authorization)
|
||||
headers.Authorization = options?.previousLoginResult?.authorization;
|
||||
const response = await axios.get(url, {
|
||||
withCredentials: true,
|
||||
headers,
|
||||
...options?.axiosConfig,
|
||||
});
|
||||
const scryptedCloud = response.headers['x-scrypted-cloud'] === 'true';
|
||||
@@ -164,6 +167,7 @@ export async function checkScryptedClientLogin(options?: ScryptedConnectionOptio
|
||||
const cloudAddress = response.headers['x-scrypted-cloud-address'];
|
||||
|
||||
return {
|
||||
baseUrl,
|
||||
hostname: response.data.hostname as string,
|
||||
redirect: response.data.redirect as string,
|
||||
username: response.data.username as string,
|
||||
@@ -180,6 +184,15 @@ export async function checkScryptedClientLogin(options?: ScryptedConnectionOptio
|
||||
};
|
||||
}
|
||||
|
||||
export interface ScryptedClientLoginResult {
|
||||
authorization: string;
|
||||
queryToken: { [parameter: string]: string };
|
||||
localAddresses: string[];
|
||||
scryptedCloud: boolean;
|
||||
directAddress: string;
|
||||
cloudAddress: string;
|
||||
}
|
||||
|
||||
export class ScryptedClientLoginError extends Error {
|
||||
constructor(public result: Awaited<ReturnType<typeof checkScryptedClientLogin>>) {
|
||||
super(result.error);
|
||||
@@ -215,10 +228,9 @@ export async function redirectScryptedLogout(baseUrl?: string) {
|
||||
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 scryptedCloud: boolean;
|
||||
let directAddress: string;
|
||||
@@ -226,6 +238,8 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
|
||||
console.log('@scrypted/client', packageJson.version);
|
||||
|
||||
const extraHeaders: { [header: string]: string } = {};
|
||||
|
||||
if (username && password) {
|
||||
const loginResult = await loginScryptedClient(options as ScryptedLoginOptions);
|
||||
if (loginResult.authorization)
|
||||
@@ -239,9 +253,47 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
console.log('login result', Date.now() - start, loginResult);
|
||||
}
|
||||
else {
|
||||
const loginCheck = await checkScryptedClientLogin({
|
||||
const urlsToCheck = new Set<string>();
|
||||
for (const u of [
|
||||
...options?.previousLoginResult?.localAddresses || [],
|
||||
options?.previousLoginResult?.directAddress,
|
||||
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);
|
||||
}
|
||||
catch (e) {
|
||||
loginCheck = await baseUrlCheck;
|
||||
}
|
||||
|
||||
if (loginCheck.error || loginCheck.redirect)
|
||||
throw new ScryptedClientLoginError(loginCheck);
|
||||
localAddresses = loginCheck.addresses;
|
||||
@@ -632,9 +684,15 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
pluginHostAPI: undefined,
|
||||
rtcConnectionManagement,
|
||||
browserSignalingSession,
|
||||
authorization,
|
||||
queryToken,
|
||||
rpcPeer,
|
||||
loginResult: {
|
||||
directAddress,
|
||||
localAddresses,
|
||||
scryptedCloud,
|
||||
queryToken,
|
||||
authorization,
|
||||
cloudAddress,
|
||||
}
|
||||
}
|
||||
|
||||
socket.on('close', () => {
|
||||
|
||||
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"
|
||||
}
|
||||
]
|
||||
}
|
||||
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();
|
||||
4
plugins/amcrest/package-lock.json
generated
4
plugins/amcrest/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.124",
|
||||
"version": "0.0.127",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.124",
|
||||
"version": "0.0.127",
|
||||
"license": "Apache",
|
||||
"dependencies": {
|
||||
"@koush/axios-digest-auth": "^0.8.5",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.124",
|
||||
"version": "0.0.127",
|
||||
"description": "Amcrest Plugin for Scrypted",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache",
|
||||
|
||||
@@ -95,6 +95,7 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
for (const element of deviceParameters) {
|
||||
try {
|
||||
const response = await this.getClient().digestAuth.request({
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
url: `http://${this.getHttpAddress()}/cgi-bin/magicBox.cgi?action=${element.action}`
|
||||
});
|
||||
|
||||
@@ -147,6 +148,7 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
return;
|
||||
|
||||
const response = await this.getClient().digestAuth.request({
|
||||
httpsAgent: amcrestHttpsAgent,
|
||||
url: `http://${this.getHttpAddress()}/cgi-bin/configManager.cgi?action=setConfig&${params}`
|
||||
});
|
||||
this.console.log('reconfigure result', response.data);
|
||||
@@ -190,14 +192,11 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
|| event === AmcrestEvent.PhoneCallDetectStart
|
||||
|| event === AmcrestEvent.AlarmIPCStart
|
||||
|| event === AmcrestEvent.DahuaTalkInvite) {
|
||||
if (event === AmcrestEvent.DahuaTalkInvite && payload && multipleCallIds)
|
||||
{
|
||||
if (payload.includes(callerId))
|
||||
{
|
||||
if (event === AmcrestEvent.DahuaTalkInvite && payload && multipleCallIds) {
|
||||
if (payload.includes(callerId)) {
|
||||
this.binaryState = true;
|
||||
}
|
||||
} else
|
||||
{
|
||||
} else {
|
||||
this.binaryState = true;
|
||||
}
|
||||
}
|
||||
@@ -259,25 +258,23 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
|
||||
if (!twoWayAudio)
|
||||
twoWayAudio = isDoorbell ? 'Amcrest' : 'None';
|
||||
|
||||
|
||||
if (doorbellType == DAHUA_DOORBELL_TYPE)
|
||||
{
|
||||
|
||||
|
||||
if (doorbellType == DAHUA_DOORBELL_TYPE) {
|
||||
ret.push(
|
||||
{
|
||||
title: 'Multiple Call Buttons',
|
||||
key: 'multipleCallIds',
|
||||
description: 'Some Dahua Doorbells integrate multiple Call Buttons for apartment buildings.',
|
||||
type: 'boolean',
|
||||
value: (this.storage.getItem('multipleCallIds') === 'true').toString(),
|
||||
}
|
||||
{
|
||||
title: 'Multiple Call Buttons',
|
||||
key: 'multipleCallIds',
|
||||
description: 'Some Dahua Doorbells integrate multiple Call Buttons for apartment buildings.',
|
||||
type: 'boolean',
|
||||
value: (this.storage.getItem('multipleCallIds') === 'true').toString(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const multipleCallIds = this.storage.getItem('multipleCallIds');
|
||||
|
||||
if (multipleCallIds)
|
||||
{
|
||||
if (multipleCallIds) {
|
||||
ret.push(
|
||||
{
|
||||
title: 'Caller ID',
|
||||
@@ -288,7 +285,7 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
ret.push(
|
||||
{
|
||||
@@ -309,11 +306,11 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
);
|
||||
|
||||
return ret;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async takeSmartCameraPicture(option?: PictureOptions): Promise<MediaObject> {
|
||||
return this.createMediaObject(await this.getClient().jpegSnapshot(), 'image/jpeg');
|
||||
@@ -401,11 +398,11 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
?.replace('.', '')?.toLowerCase()?.trim();
|
||||
if (audioCodec?.includes('aac'))
|
||||
audioCodec = 'aac';
|
||||
else if (audioCodec.includes('g711a'))
|
||||
else if (audioCodec?.includes('g711a'))
|
||||
audioCodec = 'pcm_alaw';
|
||||
else if (audioCodec.includes('g711u'))
|
||||
else if (audioCodec?.includes('g711u'))
|
||||
audioCodec = 'pcm_ulaw';
|
||||
else if (audioCodec.includes('g711'))
|
||||
else if (audioCodec?.includes('g711'))
|
||||
audioCodec = 'pcm';
|
||||
|
||||
if (vso.audio)
|
||||
@@ -490,7 +487,7 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
this.videoStreamOptions = undefined;
|
||||
|
||||
super.putSetting(key, value);
|
||||
|
||||
|
||||
this.updateDevice();
|
||||
this.updateDeviceInfo();
|
||||
}
|
||||
|
||||
4
plugins/cloud/package-lock.json
generated
4
plugins/cloud/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/cloud",
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.41",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/cloud",
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.41",
|
||||
"dependencies": {
|
||||
"@eneris/push-receiver": "^3.1.4",
|
||||
"@scrypted/common": "file:../../common",
|
||||
|
||||
@@ -54,5 +54,5 @@
|
||||
"@types/nat-upnp": "^1.1.2",
|
||||
"@types/node": "^20.4.5"
|
||||
},
|
||||
"version": "0.1.40"
|
||||
"version": "0.1.41"
|
||||
}
|
||||
|
||||
@@ -159,11 +159,20 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
this.cloudflared?.child.kill();
|
||||
},
|
||||
},
|
||||
cloudflaredTunnelUrl: {
|
||||
group: 'Advanced',
|
||||
title: 'Cloudflare Tunnel URL',
|
||||
description: 'Cloudflare Tunnel URL is a randomized cloud connection, unless a Cloudflare Tunnel Token is provided.',
|
||||
readonly: true,
|
||||
mapGet: () => this.cloudflareTunnel || 'Unavailable',
|
||||
},
|
||||
register: {
|
||||
group: 'Advanced',
|
||||
title: 'Register',
|
||||
type: 'button',
|
||||
onPut: () => this.manager.registrationId.then(r => this.sendRegistrationId(r)),
|
||||
onPut: () => {
|
||||
this.manager.registrationId.then(r => this.sendRegistrationId(r))
|
||||
},
|
||||
description: 'Register server with Scrypted Cloud.',
|
||||
},
|
||||
testPortForward: {
|
||||
@@ -173,6 +182,14 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
onPut: () => this.testPortForward(),
|
||||
description: 'Test the port forward connection from Scrypted Cloud.',
|
||||
},
|
||||
additionalCorsOrigins: {
|
||||
title: "Additional CORS Origins",
|
||||
description: "Debugging purposes only. DO NOT EDIT.",
|
||||
group: 'CORS',
|
||||
multiple: true,
|
||||
combobox: true,
|
||||
defaultValue: [],
|
||||
}
|
||||
});
|
||||
upnpInterval: NodeJS.Timeout;
|
||||
upnpClient = upnp.createClient();
|
||||
@@ -474,6 +491,7 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
`https://${SCRYPTED_SERVER}`,
|
||||
// chromecast receiver. move this into google home and chromecast plugins?
|
||||
'https://koush.github.io',
|
||||
...this.storageSettings.values.additionalCorsOrigins,
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,20 +10,29 @@
|
||||
doubleClickZoom: false,
|
||||
boxZoom: false,
|
||||
scrollWheelZoom: false,
|
||||
touchZoom: false,
|
||||
}"
|
||||
>
|
||||
<l-tile-layer :url="url" :attribution="attribution"></l-tile-layer>
|
||||
<l-marker :lat-lng="position" :icon="icon"></l-marker>
|
||||
<l-marker :lat-lng="position"></l-marker>
|
||||
<l-control-attribution position="bottomright" :prefix="prefix"></l-control-attribution>
|
||||
</l-map>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { latLng, icon } from "leaflet";
|
||||
import { latLng, Icon } from "leaflet";
|
||||
import { LMap, LTileLayer, LMarker, LControlAttribution } from "vue2-leaflet";
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import RPCInterface from "../RPCInterface.vue";
|
||||
|
||||
// https://vue2-leaflet.netlify.app/quickstart/#marker-icons-are-missing
|
||||
delete Icon.Default.prototype._getIconUrl;
|
||||
Icon.Default.mergeOptions({
|
||||
iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
|
||||
iconUrl: require('leaflet/dist/images/marker-icon.png'),
|
||||
shadowUrl: require('leaflet/dist/images/marker-shadow.png'),
|
||||
});
|
||||
|
||||
export default {
|
||||
mixins: [RPCInterface],
|
||||
components: {
|
||||
@@ -37,10 +46,6 @@ export default {
|
||||
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
prefix: '<a target="blank" href="https://leafletjs.com/">Leaflet</a>',
|
||||
attribution: '© <a target="_blank" href="http://osm.org/copyright">OpenStreetMap</a> contributors',
|
||||
icon: icon({
|
||||
iconUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png",
|
||||
shadowUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png",
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
||||
4
plugins/coreml/package-lock.json
generated
4
plugins/coreml/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/coreml",
|
||||
"version": "0.1.26",
|
||||
"version": "0.1.28",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/coreml",
|
||||
"version": "0.1.26",
|
||||
"version": "0.1.28",
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
}
|
||||
|
||||
@@ -34,11 +34,12 @@
|
||||
"type": "API",
|
||||
"interfaces": [
|
||||
"Settings",
|
||||
"ObjectDetection"
|
||||
"ObjectDetection",
|
||||
"ObjectDetectionPreview"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
},
|
||||
"version": "0.1.26"
|
||||
"version": "0.1.28"
|
||||
}
|
||||
|
||||
@@ -498,6 +498,9 @@ export class H264Repacketizer {
|
||||
// after the codec information. so codec information can be changed between
|
||||
// idr and non-idr? maybe it is not applied until next idr?
|
||||
}
|
||||
else if (nalType === 0) {
|
||||
// nal delimiter or something. usually empty.
|
||||
}
|
||||
else {
|
||||
this.console.warn('Skipped a stapa type. Please report this to @koush on Discord.', nalType)
|
||||
}
|
||||
|
||||
4
plugins/objectdetector/package-lock.json
generated
4
plugins/objectdetector/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/objectdetector",
|
||||
"version": "0.0.164",
|
||||
"version": "0.1.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/objectdetector",
|
||||
"version": "0.0.164",
|
||||
"version": "0.1.1",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/objectdetector",
|
||||
"version": "0.0.164",
|
||||
"version": "0.1.1",
|
||||
"description": "Scrypted Video Analysis Plugin. Installed alongside a detection service like OpenCV or TensorFlow.",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -161,7 +161,7 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
|
||||
maybeStartDetection() {
|
||||
if (!this.hasMotionType) {
|
||||
// object detection may be restarted if there are slots available.
|
||||
if (this.cameraDevice.motionDetected && this.plugin.canStartObjectDetection())
|
||||
if (this.cameraDevice.motionDetected && this.plugin.canStartObjectDetection(this))
|
||||
this.startPipelineAnalysis();
|
||||
return;
|
||||
}
|
||||
@@ -1022,6 +1022,12 @@ class ObjectDetectionPlugin extends AutoenableMixinProvider implements Settings,
|
||||
return value;
|
||||
},
|
||||
},
|
||||
developerMode: {
|
||||
group: 'Advanced',
|
||||
title: 'Developer Mode',
|
||||
description: 'Developer mode enables usage of the raw detector object detectors. Using raw object detectors (ie, outside of Scrypted NVR) can cause severe performance degradation.',
|
||||
type: 'boolean',
|
||||
}
|
||||
});
|
||||
|
||||
shouldUseSnapshotPipeline() {
|
||||
@@ -1068,15 +1074,27 @@ class ObjectDetectionPlugin extends AutoenableMixinProvider implements Settings,
|
||||
return maxConcurrent;
|
||||
}
|
||||
|
||||
canStartObjectDetection() {
|
||||
canStartObjectDetection(mixin: ObjectDetectionMixin) {
|
||||
const maxConcurrent = this.maxConcurrent;
|
||||
|
||||
const objectDetections = [...this.currentMixins.values()]
|
||||
const runningDetections = [...this.currentMixins.values()]
|
||||
.map(d => [...d.currentMixins.values()].filter(dd => !dd.hasMotionType)).flat()
|
||||
.filter(c => c.detectorRunning)
|
||||
.sort((a, b) => a.detectionStartTime - b.detectionStartTime);
|
||||
|
||||
return objectDetections.length < maxConcurrent;
|
||||
// already running
|
||||
if (runningDetections.find(o => o.id === mixin.id))
|
||||
return false;
|
||||
|
||||
if (runningDetections.length < maxConcurrent)
|
||||
return true;
|
||||
|
||||
const [first] = runningDetections;
|
||||
if (Date.now() - first.detectionStartTime > 30000)
|
||||
return true;
|
||||
|
||||
mixin.console.log(`Not starting object detection to continue processing recent activity on ${first.name}.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
objectDetectionStarted(name: string, console: Console) {
|
||||
@@ -1143,7 +1161,7 @@ class ObjectDetectionPlugin extends AutoenableMixinProvider implements Settings,
|
||||
}
|
||||
|
||||
constructor(nativeId?: ScryptedNativeId) {
|
||||
super(nativeId);
|
||||
super(nativeId, 'v5');
|
||||
|
||||
process.nextTick(() => {
|
||||
sdk.deviceManager.onDevicesChanged({
|
||||
@@ -1180,6 +1198,8 @@ class ObjectDetectionPlugin extends AutoenableMixinProvider implements Settings,
|
||||
async canMixin(type: ScryptedDeviceType, interfaces: string[]): Promise<string[]> {
|
||||
if (!interfaces.includes(ScryptedInterface.ObjectDetection))
|
||||
return;
|
||||
if (!this.storageSettings.values.developerMode && !interfaces.includes(ScryptedInterface.ObjectDetectionGenerator))
|
||||
return;
|
||||
return [ScryptedInterface.MixinProvider];
|
||||
}
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ export class OnvifIntercom implements Intercom {
|
||||
let pending: RtpPacket;
|
||||
let seqNumber = 0;
|
||||
|
||||
const forwarder = await startRtpForwarderProcess(console, ffmpegInput, {
|
||||
const forwarder = await startRtpForwarderProcess(this.camera.console, ffmpegInput, {
|
||||
audio: {
|
||||
onRtp: (rtp) => {
|
||||
// if (true) {
|
||||
|
||||
4
plugins/opencv/package-lock.json
generated
4
plugins/opencv/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/opencv",
|
||||
"version": "0.0.87",
|
||||
"version": "0.0.89",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/opencv",
|
||||
"version": "0.0.87",
|
||||
"version": "0.0.89",
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
"runtime": "python",
|
||||
"type": "API",
|
||||
"interfaces": [
|
||||
"ObjectDetection"
|
||||
"ObjectDetection",
|
||||
"ObjectDetectionGenerator"
|
||||
],
|
||||
"pluginDependencies": [
|
||||
"@scrypted/objectdetector",
|
||||
@@ -36,5 +37,5 @@
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
},
|
||||
"version": "0.0.87"
|
||||
"version": "0.0.89"
|
||||
}
|
||||
|
||||
4
plugins/openvino/package-lock.json
generated
4
plugins/openvino/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/openvino",
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.42",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/openvino",
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.42",
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
}
|
||||
|
||||
@@ -34,12 +34,13 @@
|
||||
"runtime": "python",
|
||||
"type": "API",
|
||||
"interfaces": [
|
||||
"Settings",
|
||||
"ObjectDetection",
|
||||
"Settings"
|
||||
"ObjectDetectionPreview"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
},
|
||||
"version": "0.1.40"
|
||||
"version": "0.1.42"
|
||||
}
|
||||
|
||||
4
plugins/pam-diff/package-lock.json
generated
4
plugins/pam-diff/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/pam-diff",
|
||||
"version": "0.0.23",
|
||||
"version": "0.0.24",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/pam-diff",
|
||||
"version": "0.0.23",
|
||||
"version": "0.0.24",
|
||||
"dependencies": {
|
||||
"@types/node": "^16.6.1",
|
||||
"pipe2pam": "^0.6.2"
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
"name": "PAM Diff Motion Detection",
|
||||
"type": "API",
|
||||
"interfaces": [
|
||||
"ObjectDetection"
|
||||
"ObjectDetection",
|
||||
"ObjectDetectionGenerator"
|
||||
],
|
||||
"pluginDependencies": [
|
||||
"@scrypted/objectdetector"
|
||||
@@ -43,5 +44,5 @@
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
},
|
||||
"version": "0.0.23"
|
||||
"version": "0.0.24"
|
||||
}
|
||||
|
||||
4
plugins/python-codecs/package-lock.json
generated
4
plugins/python-codecs/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/python-codecs",
|
||||
"version": "0.1.84",
|
||||
"version": "0.1.85",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/python-codecs",
|
||||
"version": "0.1.84",
|
||||
"version": "0.1.85",
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/python-codecs",
|
||||
"version": "0.1.84",
|
||||
"version": "0.1.85",
|
||||
"description": "Python Codecs for Scrypted",
|
||||
"keywords": [
|
||||
"scrypted",
|
||||
|
||||
@@ -60,7 +60,7 @@ class VipsImage(scrypted_sdk.Image):
|
||||
return memoryview(gray.write_to_memory())
|
||||
return await to_thread(format)
|
||||
|
||||
return await to_thread(lambda: vipsImage.vipsImage.write_to_buffer('.' + options['format']))
|
||||
return await to_thread(lambda: vipsImage.vipsImage.write_to_buffer(f'.{options["format"]}[Q=80]'))
|
||||
|
||||
async def toImageInternal(self, options: scrypted_sdk.ImageOptions = None):
|
||||
return await to_thread(lambda: toVipsImage(self, options))
|
||||
|
||||
2
plugins/snapshot/.vscode/launch.json
vendored
2
plugins/snapshot/.vscode/launch.json
vendored
@@ -10,7 +10,7 @@
|
||||
"port": 10081,
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"**/plugin-remote-worker.*",
|
||||
"**/plugin-console.*",
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"preLaunchTask": "scrypted: deploy+debug",
|
||||
|
||||
@@ -3,3 +3,12 @@
|
||||
The Snapshot Plugin is a core plugin that provides video to image conversion and image manipulation functionality.
|
||||
|
||||
This plugin can be enabled on cameras to add custom snapshot URLs, crop and scale the snapshots, and improves camera snapshot responsiveness.
|
||||
|
||||
## Core Features
|
||||
|
||||
* Shrink images for better transfer rate on mobile networks.
|
||||
* Custom Snapshot URLs for RTSP cameras.
|
||||
* Cache snapshots for better responsiveness.
|
||||
* Snapshot error recovery.
|
||||
* Convert video streams to images for cameras without snapshot URLs.
|
||||
* Resize image service for other plugins.
|
||||
|
||||
4
plugins/snapshot/package-lock.json
generated
4
plugins/snapshot/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/snapshot",
|
||||
"version": "0.0.60",
|
||||
"version": "0.0.62",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/snapshot",
|
||||
"version": "0.0.60",
|
||||
"version": "0.0.62",
|
||||
"dependencies": {
|
||||
"@koush/axios-digest-auth": "^0.8.5",
|
||||
"@types/node": "^18.16.18",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/snapshot",
|
||||
"version": "0.0.60",
|
||||
"version": "0.0.62",
|
||||
"description": "Snapshot Plugin for Scrypted",
|
||||
"scripts": {
|
||||
"scrypted-setup-project": "scrypted-setup-project",
|
||||
|
||||
4
plugins/tensorflow-lite/package-lock.json
generated
4
plugins/tensorflow-lite/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/tensorflow-lite",
|
||||
"version": "0.1.42",
|
||||
"version": "0.1.44",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/tensorflow-lite",
|
||||
"version": "0.1.42",
|
||||
"version": "0.1.44",
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
}
|
||||
|
||||
@@ -47,11 +47,12 @@
|
||||
"type": "API",
|
||||
"interfaces": [
|
||||
"Settings",
|
||||
"ObjectDetection"
|
||||
"ObjectDetection",
|
||||
"ObjectDetectionPreview"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
},
|
||||
"version": "0.1.42"
|
||||
"version": "0.1.44"
|
||||
}
|
||||
|
||||
4
plugins/tensorflow/package-lock.json
generated
4
plugins/tensorflow/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/tensorflow-lite",
|
||||
"version": "0.1.16",
|
||||
"version": "0.1.18",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/tensorflow-lite",
|
||||
"version": "0.1.16",
|
||||
"version": "0.1.18",
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
}
|
||||
|
||||
@@ -35,11 +35,12 @@
|
||||
"type": "API",
|
||||
"interfaces": [
|
||||
"Settings",
|
||||
"ObjectDetection"
|
||||
"ObjectDetection",
|
||||
"ObjectDetectionPreview"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
},
|
||||
"version": "0.1.16"
|
||||
"version": "0.1.18"
|
||||
}
|
||||
|
||||
4
plugins/webrtc/package-lock.json
generated
4
plugins/webrtc/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/webrtc",
|
||||
"version": "0.1.71",
|
||||
"version": "0.1.72",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/webrtc",
|
||||
"version": "0.1.71",
|
||||
"version": "0.1.72",
|
||||
"dependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/webrtc",
|
||||
"version": "0.1.71",
|
||||
"version": "0.1.72",
|
||||
"scripts": {
|
||||
"scrypted-setup-project": "scrypted-setup-project",
|
||||
"prescrypted-setup-project": "scrypted-package-json",
|
||||
|
||||
@@ -161,7 +161,7 @@ export class StorageSettings<T extends string> implements Settings {
|
||||
this.device.onDeviceEvent(ScryptedInterface.Settings, undefined);
|
||||
}
|
||||
|
||||
private getItemInternal(key: T, setting: StorageSetting, rawDevice?: boolean): any {
|
||||
getItemInternal(key: T, setting: StorageSetting, rawDevice?: boolean): any {
|
||||
if (!setting)
|
||||
return this.device.storage.getItem(key);
|
||||
const readDefaultValue = () => {
|
||||
|
||||
@@ -146,6 +146,8 @@ class ScryptedInterface(str, Enum):
|
||||
Notifier = "Notifier"
|
||||
OauthClient = "OauthClient"
|
||||
ObjectDetection = "ObjectDetection"
|
||||
ObjectDetectionGenerator = "ObjectDetectionGenerator"
|
||||
ObjectDetectionPreview = "ObjectDetectionPreview"
|
||||
ObjectDetector = "ObjectDetector"
|
||||
ObjectTracker = "ObjectTracker"
|
||||
OccupancySensor = "OccupancySensor"
|
||||
@@ -1109,6 +1111,16 @@ class ObjectDetection:
|
||||
pass
|
||||
|
||||
|
||||
class ObjectDetectionGenerator:
|
||||
|
||||
|
||||
pass
|
||||
|
||||
class ObjectDetectionPreview:
|
||||
|
||||
|
||||
pass
|
||||
|
||||
class ObjectDetector:
|
||||
"""ObjectDetector is found on Cameras that have smart detection capabilities."""
|
||||
|
||||
@@ -2913,6 +2925,16 @@ ScryptedInterfaceDescriptors = {
|
||||
],
|
||||
"properties": []
|
||||
},
|
||||
"ObjectDetectionPreview": {
|
||||
"name": "ObjectDetectionPreview",
|
||||
"methods": [],
|
||||
"properties": []
|
||||
},
|
||||
"ObjectDetectionGenerator": {
|
||||
"name": "ObjectDetectionGenerator",
|
||||
"methods": [],
|
||||
"properties": []
|
||||
},
|
||||
"HumiditySetting": {
|
||||
"name": "HumiditySetting",
|
||||
"methods": [
|
||||
|
||||
@@ -21,15 +21,16 @@ function toTypescriptType(type: any): string {
|
||||
|
||||
for (const name of Object.values(ScryptedInterface)) {
|
||||
const td = schema.children.find((child: any) => child.name === name);
|
||||
const properties = td.children.filter((child: any) => child.kindString === 'Property').map((child: any) => child.name);
|
||||
const methods = td.children.filter((child: any) => child.kindString === 'Method').map((child: any) => child.name);
|
||||
const children = td.children || [];
|
||||
const properties = children.filter((child: any) => child.kindString === 'Property').map((child: any) => child.name);
|
||||
const methods = children.filter((child: any) => child.kindString === 'Method').map((child: any) => child.name);
|
||||
ScryptedInterfaceDescriptors[name] = {
|
||||
name,
|
||||
methods,
|
||||
properties,
|
||||
};
|
||||
|
||||
for (const p of td.children.filter((child: any) => child.kindString === 'Property')) {
|
||||
for (const p of children.filter((child: any) => child.kindString === 'Property')) {
|
||||
allProperties[p.name] = p.type;
|
||||
}
|
||||
}
|
||||
@@ -196,8 +197,9 @@ class ${td.name}:
|
||||
${toDocstring(td)}
|
||||
`;
|
||||
|
||||
const properties = td.children.filter((child: any) => child.kindString === 'Property');
|
||||
const methods = td.children.filter((child: any) => child.kindString === 'Method');
|
||||
const children = td.children || [];
|
||||
const properties = children.filter((child: any) => child.kindString === 'Property');
|
||||
const methods = children.filter((child: any) => child.kindString === 'Method');
|
||||
for (const property of properties) {
|
||||
python += ` ${property.name}: ${toPythonType(property.type)}${toComment(property)}
|
||||
`
|
||||
@@ -208,6 +210,10 @@ ${toDocstring(td)}
|
||||
|
||||
`
|
||||
}
|
||||
if (!td.children)
|
||||
python += `
|
||||
pass
|
||||
`
|
||||
}
|
||||
|
||||
for (const td of interfaces) {
|
||||
|
||||
@@ -1383,6 +1383,10 @@ export interface ObjectDetection {
|
||||
detectObjects(mediaObject: MediaObject, session?: ObjectDetectionSession): Promise<ObjectsDetected>;
|
||||
getDetectionModel(settings?: { [key: string]: any }): Promise<ObjectDetectionModel>;
|
||||
}
|
||||
export interface ObjectDetectionPreview {
|
||||
}
|
||||
export interface ObjectDetectionGenerator {
|
||||
}
|
||||
export type ImageFormat = 'gray' | 'rgba' | 'rgb' | 'jpg';
|
||||
export interface ImageOptions {
|
||||
crop?: {
|
||||
@@ -2006,6 +2010,8 @@ export enum ScryptedInterface {
|
||||
ObjectTracker = "ObjectTracker",
|
||||
ObjectDetector = "ObjectDetector",
|
||||
ObjectDetection = "ObjectDetection",
|
||||
ObjectDetectionPreview = "ObjectDetectionPreview",
|
||||
ObjectDetectionGenerator = "ObjectDetectionGenerator",
|
||||
HumiditySetting = "HumiditySetting",
|
||||
Fan = "Fan",
|
||||
RTCSignalingChannel = "RTCSignalingChannel",
|
||||
|
||||
1
server/.vscode/launch.json
vendored
1
server/.vscode/launch.json
vendored
@@ -15,7 +15,6 @@
|
||||
"preLaunchTask": "npm: build",
|
||||
"program": "${workspaceFolder}/bin/scrypted-serve",
|
||||
"runtimeArgs": [
|
||||
"--dns-result-order=ipv4first",
|
||||
"--trace-warnings",
|
||||
"--nolazy",
|
||||
],
|
||||
|
||||
98
server/package-lock.json
generated
98
server/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/server",
|
||||
"version": "0.50.0",
|
||||
"version": "0.51.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/server",
|
||||
"version": "0.50.0",
|
||||
"version": "0.51.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@mapbox/node-pre-gyp": "^1.0.11",
|
||||
@@ -18,14 +18,15 @@
|
||||
"engine.io": "^6.5.2",
|
||||
"express": "^4.18.2",
|
||||
"ffmpeg-static": "^5.2.0",
|
||||
"follow-redirects": "^1.15.3",
|
||||
"http-auth": "^4.2.0",
|
||||
"ip": "^1.1.8",
|
||||
"level": "^8.0.0",
|
||||
"linkfs": "^2.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"memfs": "^4.2.1",
|
||||
"memfs": "^4.3.0",
|
||||
"mime": "^3.0.0",
|
||||
"nan": "^2.17.0",
|
||||
"nan": "^2.18.0",
|
||||
"node-dijkstra": "^2.5.0",
|
||||
"node-forge": "^1.3.1",
|
||||
"node-gyp": "^9.4.0",
|
||||
@@ -36,26 +37,27 @@
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.2.2",
|
||||
"whatwg-mimetype": "^3.0.0",
|
||||
"ws": "^8.14.1"
|
||||
"ws": "^8.14.2"
|
||||
},
|
||||
"bin": {
|
||||
"scrypted-serve": "bin/scrypted-serve"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/adm-zip": "^0.5.0",
|
||||
"@types/adm-zip": "^0.5.1",
|
||||
"@types/cookie-parser": "^1.4.4",
|
||||
"@types/debug": "^4.1.8",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/http-auth": "^4.1.1",
|
||||
"@types/follow-redirects": "^1.14.2",
|
||||
"@types/http-auth": "^4.1.2",
|
||||
"@types/ip": "^1.1.0",
|
||||
"@types/lodash": "^4.14.198",
|
||||
"@types/mime": "^3.0.1",
|
||||
"@types/node-dijkstra": "^2.5.3",
|
||||
"@types/node-forge": "^1.3.5",
|
||||
"@types/pem": "^1.14.0",
|
||||
"@types/semver": "^7.5.1",
|
||||
"@types/pem": "^1.14.1",
|
||||
"@types/semver": "^7.5.2",
|
||||
"@types/source-map-support": "^0.5.7",
|
||||
"@types/tar": "^6.1.5",
|
||||
"@types/tar": "^6.1.6",
|
||||
"@types/whatwg-mimetype": "^3.0.0",
|
||||
"@types/ws": "^8.5.5"
|
||||
},
|
||||
@@ -192,9 +194,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/adm-zip": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.0.tgz",
|
||||
"integrity": "sha512-FCJBJq9ODsQZUNURo5ILAQueuA8WJhRvuihS3ke2iI25mJlfV2LK8jG2Qj2z2AWg8U0FtWWqBHVRetceLskSaw==",
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.1.tgz",
|
||||
"integrity": "sha512-3+psmbh60N5JXM2LMkujFqnjMf3KB0LZoIQO73NJvkv57q+384nK/A7pP0v+ZkB/Zrfqn+5xtAyt5OsY+GiYLQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
@@ -274,10 +276,19 @@
|
||||
"@types/send": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/follow-redirects": {
|
||||
"version": "1.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/follow-redirects/-/follow-redirects-1.14.2.tgz",
|
||||
"integrity": "sha512-NfvpNEAyiTvM+Wq4iTrSCyuryLG+ZIrnhUbvgakBxMiTY4Q959jX9GHGXm0p/jQgry1hjd7c2eRjT2NJX1m5uQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/http-auth": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-auth/-/http-auth-4.1.1.tgz",
|
||||
"integrity": "sha512-oBOiSe402K7P2yoCDLctjFOoUfiBBbvoZ3RVRr3vZg7R/RW2U+BbYR2MYFNrQ6S3Kwtvt893DowfNe9g5LyIXQ==",
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-auth/-/http-auth-4.1.2.tgz",
|
||||
"integrity": "sha512-MBN/yFiZ3dofn3YO4gX8boXEpD1KDgcYDkr7BHIBIUhABFMlpeAro08a2l2rO7BBxJmo2dW8Tp9Zu9ISLJlp0w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
@@ -331,9 +342,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pem": {
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/pem/-/pem-1.14.0.tgz",
|
||||
"integrity": "sha512-Jaq0lQWgz2jVmQ+smGDbPnLye6DOLojykwmnGgveoYb3sjZfrxO+2ssbccbekKKCTFxoLSEGCP/48z9FPjAbJg==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/pem/-/pem-1.14.1.tgz",
|
||||
"integrity": "sha512-blRIGpofJKMV7v6UnS/R75AUAqLfla212lN/7TXYtTcGSqvMv84Aiwl/xg8MirahHTvDABRV8PdnCT1fVZW09g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
@@ -352,9 +363,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz",
|
||||
"integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==",
|
||||
"version": "7.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.2.tgz",
|
||||
"integrity": "sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/send": {
|
||||
@@ -393,9 +404,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/tar": {
|
||||
"version": "6.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/tar/-/tar-6.1.5.tgz",
|
||||
"integrity": "sha512-qm2I/RlZij5RofuY7vohTpYNaYcrSQlN2MyjucQc7ZweDwaEWkdN/EeNh6e9zjK6uEm6PwjdMXkcj05BxZdX1Q==",
|
||||
"version": "6.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/tar/-/tar-6.1.6.tgz",
|
||||
"integrity": "sha512-HQ06kiiDXz9uqtmE9ksQUn1ovcPr1gGV9EgaCWo6FGYKD0onNBCetBzL0kfcS8Kbj1EFxJWY9jL2W4ZvvtGI8Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
@@ -1355,6 +1366,25 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.3",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
|
||||
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
|
||||
@@ -1925,9 +1955,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/memfs": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-4.2.1.tgz",
|
||||
"integrity": "sha512-CINEB6cNAAhLUfRGrB4lj2Pj47ygerEmw3jxPb6R1gkD6Jfp484gJLteQ6MzqIjGWtFWuVzDl+KN7HiipMuKSw==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-4.3.0.tgz",
|
||||
"integrity": "sha512-bOMyYalKCLeEkd5l3sYv0XIsVPQW+2oGhYm8LwD1htXS637VmIdoXVrWPxZdbJlEogDIrTnm6wqqZBrYb7ZFPw==",
|
||||
"dependencies": {
|
||||
"json-joy": "^9.2.0",
|
||||
"thingies": "^1.11.1"
|
||||
@@ -2193,9 +2223,9 @@
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.17.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz",
|
||||
"integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ=="
|
||||
"version": "2.18.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz",
|
||||
"integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w=="
|
||||
},
|
||||
"node_modules/napi-build-utils": {
|
||||
"version": "1.0.2",
|
||||
@@ -3589,9 +3619,9 @@
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.14.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.14.1.tgz",
|
||||
"integrity": "sha512-4OOseMUq8AzRBI/7SLMUwO+FEDnguetSk7KMb1sHwvF2w2Wv5Hoj0nlifx8vtGsftE/jWHojPy8sMMzYLJ2G/A==",
|
||||
"version": "8.14.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
|
||||
"integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/server",
|
||||
"version": "0.50.0",
|
||||
"version": "0.51.0",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@mapbox/node-pre-gyp": "^1.0.11",
|
||||
@@ -12,14 +12,15 @@
|
||||
"engine.io": "^6.5.2",
|
||||
"express": "^4.18.2",
|
||||
"ffmpeg-static": "^5.2.0",
|
||||
"follow-redirects": "^1.15.3",
|
||||
"http-auth": "^4.2.0",
|
||||
"ip": "^1.1.8",
|
||||
"level": "^8.0.0",
|
||||
"linkfs": "^2.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"memfs": "^4.2.1",
|
||||
"memfs": "^4.3.0",
|
||||
"mime": "^3.0.0",
|
||||
"nan": "^2.17.0",
|
||||
"nan": "^2.18.0",
|
||||
"node-dijkstra": "^2.5.0",
|
||||
"node-forge": "^1.3.1",
|
||||
"node-gyp": "^9.4.0",
|
||||
@@ -30,23 +31,24 @@
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.2.2",
|
||||
"whatwg-mimetype": "^3.0.0",
|
||||
"ws": "^8.14.1"
|
||||
"ws": "^8.14.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/adm-zip": "^0.5.0",
|
||||
"@types/adm-zip": "^0.5.1",
|
||||
"@types/cookie-parser": "^1.4.4",
|
||||
"@types/debug": "^4.1.8",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/http-auth": "^4.1.1",
|
||||
"@types/follow-redirects": "^1.14.2",
|
||||
"@types/http-auth": "^4.1.2",
|
||||
"@types/ip": "^1.1.0",
|
||||
"@types/lodash": "^4.14.198",
|
||||
"@types/mime": "^3.0.1",
|
||||
"@types/node-dijkstra": "^2.5.3",
|
||||
"@types/node-forge": "^1.3.5",
|
||||
"@types/pem": "^1.14.0",
|
||||
"@types/semver": "^7.5.1",
|
||||
"@types/pem": "^1.14.1",
|
||||
"@types/semver": "^7.5.2",
|
||||
"@types/source-map-support": "^0.5.7",
|
||||
"@types/tar": "^6.1.5",
|
||||
"@types/tar": "^6.1.6",
|
||||
"@types/whatwg-mimetype": "^3.0.0",
|
||||
"@types/ws": "^8.5.5"
|
||||
},
|
||||
|
||||
@@ -1,11 +1,61 @@
|
||||
import { once } from 'events';
|
||||
import { http, https } from 'follow-redirects';
|
||||
import { RequestOptions, IncomingMessage } from 'http';
|
||||
|
||||
export async function getNpmPackageInfo(pkg: string) {
|
||||
return fetchJSON(`https://registry.npmjs.org/${pkg}`);
|
||||
const { json } = await fetchJSON(`https://registry.npmjs.org/${pkg}`, {
|
||||
// force ipv4 in case of busted ipv6.
|
||||
family: 4,
|
||||
});
|
||||
return json;
|
||||
}
|
||||
|
||||
export async function fetchJSON(url: string, init?: RequestInit) {
|
||||
return await (await fetch(url, init)).json();
|
||||
export async function fetchJSON(url: string, init?: RequestOptions) {
|
||||
init ||= {};
|
||||
init.headers = {
|
||||
...init.headers,
|
||||
Accept: 'application/json',
|
||||
};
|
||||
const { body, headers } = await fetchBuffer(url, init);
|
||||
return {
|
||||
json: JSON.parse(body.toString()),
|
||||
headers,
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchBuffer(url: string, init?: RequestInit) {
|
||||
return Buffer.from(await (await fetch(url, init)).arrayBuffer());
|
||||
export async function fetchPostJSON(url: string, postBody: any, init?: RequestOptions) {
|
||||
init ||= {};
|
||||
init.method = 'POST';
|
||||
init.headers = {
|
||||
...init.headers,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
const { body, headers } = await fetchBuffer(url, init, Buffer.from(JSON.stringify(postBody)));
|
||||
return {
|
||||
json: JSON.parse(body.toString()),
|
||||
headers,
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchBuffer(url: string, init?: RequestOptions, body?: Buffer) {
|
||||
const proto = url.startsWith('https:') ? https : http;
|
||||
|
||||
const request = proto.request(url, {
|
||||
...init,
|
||||
});
|
||||
if (body)
|
||||
request.write(body);
|
||||
request.end();
|
||||
const [response] = await once(request, 'response') as IncomingMessage[];
|
||||
|
||||
const buffers: Buffer[] = [];
|
||||
response.on('data', buffer => buffers.push(buffer));
|
||||
await once(response, 'end');
|
||||
|
||||
return {
|
||||
headers: response.headers,
|
||||
body: Buffer.concat(buffers),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -510,7 +510,10 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
||||
}
|
||||
console.log('installing package', pkg, version);
|
||||
|
||||
const tarball = await fetchBuffer(`${registry.versions[version].dist.tarball}`);
|
||||
const { body: tarball } = await fetchBuffer(`${registry.versions[version].dist.tarball}`, {
|
||||
// force ipv4 in case of busted ipv6.
|
||||
family: 4,
|
||||
});
|
||||
console.log('downloaded tarball', tarball?.length);
|
||||
const parse = new (tar.Parse as any)();
|
||||
const files: { [name: string]: Buffer } = {};
|
||||
|
||||
Reference in New Issue
Block a user