Compare commits

..

12 Commits

Author SHA1 Message Date
Koushik Dutta
43e5822c93 server: fix first run account creation bug 2023-04-05 14:39:20 -07:00
Koushik Dutta
bc579514e7 python-codecs: add numpy to requirements.txt 2023-04-05 11:55:04 -07:00
Koushik Dutta
825100f94e webrtc: add answer only option 2023-04-05 10:17:17 -07:00
Koushik Dutta
803bfc1560 pam-diff: tweak default motion percent 2023-04-05 10:16:46 -07:00
Koushik Dutta
b2013a54ed pam-diff: tweak default motion percent 2023-04-05 10:15:40 -07:00
Koushik Dutta
f252407935 rebroadcast: fix settings clear issue 2023-04-04 11:37:37 -07:00
Koushik Dutta
516f2a2a7b server: fetch version from package registry 2023-04-04 10:14:31 -07:00
Koushik Dutta
c1677ce691 postrelease 2023-04-04 09:59:39 -07:00
Koushik Dutta
5028fb812d server: storage polyfill should serialize keys and values as strings 2023-04-04 09:58:51 -07:00
Koushik Dutta
2db4e2579f server: add more files to .npmignore 2023-04-04 08:24:23 -07:00
Koushik Dutta
b339ca6cd2 fix bug where deleted users have continued/escalated permissions 2023-04-04 08:17:44 -07:00
Koushik Dutta
f100999cb1 postrelease 2023-04-04 08:17:13 -07:00
24 changed files with 125 additions and 50 deletions

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/pam-diff",
"version": "0.0.18",
"version": "0.0.20",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/pam-diff",
"version": "0.0.18",
"version": "0.0.20",
"hasInstallScript": true,
"dependencies": {
"@types/node": "^16.6.1",

View File

@@ -43,5 +43,5 @@
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
},
"version": "0.0.18"
"version": "0.0.20"
}

View File

@@ -9,7 +9,7 @@ import { PassThrough, Writable } from 'stream';
const { mediaManager } = sdk;
const defaultDifference = 9;
const defaultPercentage = 15;
const defaultPercentage = 2;
interface PamDiffSession {
id: string;

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/prebuffer-mixin",
"version": "0.9.79",
"version": "0.9.80",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/prebuffer-mixin",
"version": "0.9.79",
"version": "0.9.80",
"license": "Apache-2.0",
"dependencies": {
"@scrypted/common": "file:../../common",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/prebuffer-mixin",
"version": "0.9.79",
"version": "0.9.80",
"description": "Video Stream Rebroadcast, Prebuffer, and Management Plugin for Scrypted.",
"author": "Scrypted",
"license": "Apache-2.0",

View File

@@ -111,7 +111,7 @@ export function createStreamSettings(device: MixinDeviceBase<VideoCamera>) {
placeholder: '-hwaccel auto',
choices: Object.keys(getH264DecoderArgs()),
combobox: true,
mapPut: (oldValue, newValue) => getH264DecoderArgs()[newValue]?.join(' ') || newValue,
mapPut: (oldValue, newValue) => getH264DecoderArgs()[newValue]?.join(' ') || newValue || '',
hide: true,
},
videoFilterArguments: {

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/python-codecs",
"version": "0.1.27",
"version": "0.1.30",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@scrypted/python-codecs",
"version": "0.1.27",
"version": "0.1.30",
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/python-codecs",
"version": "0.1.27",
"version": "0.1.30",
"description": "Python Codecs for Scrypted",
"keywords": [
"scrypted",

View File

@@ -1,3 +1,4 @@
import traceback
import asyncio
import scrypted_sdk
from scrypted_sdk import Setting, SettingValue
@@ -6,6 +7,7 @@ import gstreamer
import libav
import vipsimage
import pilimage
import time
Gst = None
try:
@@ -128,19 +130,29 @@ def create_scrypted_plugin():
class CodecFork:
async def generateVideoFramesGstreamer(self, mediaObject: scrypted_sdk.MediaObject, options: scrypted_sdk.VideoFrameGeneratorOptions = None, filter: Any = None, h264Decoder: str = None) -> scrypted_sdk.VideoFrame:
start = time.time()
try:
async for data in gstreamer.generateVideoFramesGstreamer(mediaObject, options, filter, h264Decoder):
yield data
except Exception as e:
traceback.print_exc()
raise
finally:
print('gstreamer finished after %s' % (time.time() - start))
import os
os._exit(os.EX_OK)
pass
async def generateVideoFramesLibav(self, mediaObject: scrypted_sdk.MediaObject, options: scrypted_sdk.VideoFrameGeneratorOptions = None, filter: Any = None) -> scrypted_sdk.VideoFrame:
start = time.time()
try:
async for data in libav.generateVideoFramesLibav(mediaObject, options, filter):
yield data
except Exception as e:
traceback.print_exc()
raise
finally:
print('libav finished after %s' % (time.time() - start))
import os
os._exit(os.EX_OK)
pass

View File

@@ -1,3 +1,6 @@
# needed by libav to_ndarray
numpy>=1.16.2
# gobject instrospection for gstreamer.
PyGObject>=3.30.4; sys_platform != 'win32'

View File

@@ -1,4 +1,4 @@
{
"scrypted.debugHost": "127.0.0.1",
"scrypted.debugHost": "koushik-ubuntu",
}

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/webrtc",
"version": "0.1.40",
"version": "0.1.41",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/webrtc",
"version": "0.1.40",
"version": "0.1.41",
"dependencies": {
"@scrypted/common": "file:../../common",
"@scrypted/sdk": "file:../../sdk",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/webrtc",
"version": "0.1.40",
"version": "0.1.41",
"scripts": {
"scrypted-setup-project": "scrypted-setup-project",
"prescrypted-setup-project": "scrypted-package-json",

View File

@@ -128,6 +128,7 @@ class WebRTCMixin extends SettingsMixinDeviceBase<RTCSignalingClient & VideoCame
this.plugin.storageSettings.values.maximumCompatibilityMode,
this.plugin.getRTCConfiguration(),
await this.plugin.getWeriftConfiguration(options?.disableTurn),
options?.requiresAnswer === true ? false : true,
);
}

View File

@@ -1967,6 +1967,7 @@ export interface RTCSignalingOptions {
*/
offer?: RTCSessionDescriptionInit;
requiresOffer?: boolean;
requiresAnswer?: boolean;
/**
* Disables trickle ICE. All candidates must be sent in the initial offer/answer sdp.
*/

View File

@@ -10,3 +10,6 @@ scrypted.db.bak
__pycache__
.venv
.vscode
tsconfig.json
test
scripts

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/server",
"version": "0.7.43",
"version": "0.7.46",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/server",
"version": "0.7.43",
"version": "0.7.46",
"license": "ISC",
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.10",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/server",
"version": "0.7.44",
"version": "0.7.46",
"description": "",
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.10",

View File

@@ -1,2 +1,9 @@
const packageJson = require('../package.json');
console.log(packageJson.version);
async function main() {
const response = await fetch('https://registry.npmjs.org/@scrypted/server');
const json = await response.json();
console.log(json['dist-tags'].latest);
// const packageJson = require('../package.json');
// console.log(packageJson.version);
}
main();

View File

@@ -279,6 +279,15 @@ export class DeviceManagerImpl implements DeviceManager {
}
}
function toStorageString(value: any) {
if (value === null)
return 'null';
if (value === undefined)
return 'undefined;'
return value.toString();
}
class StorageImpl implements Storage {
api: PluginAPI;
[name: string]: any;
@@ -293,17 +302,17 @@ class StorageImpl implements Storage {
];
private static indexedHandler: ProxyHandler<StorageImpl> = {
get(target, property) {
if (StorageImpl.allowedMethods.includes(property.toString())) {
const prop = property.toString();
const f = target[property.toString()];
if (prop === 'length')
const keyString = property.toString();
if (StorageImpl.allowedMethods.includes(keyString)) {
const f = target[keyString];
if (keyString === 'length')
return f;
return f.bind(target);
}
return target.getItem(property.toString());
return target.getItem(toStorageString(property));
},
set(target, property, value): boolean {
target.setItem(property.toString(), value);
target.setItem(toStorageString(property), value);
return true;
}
};
@@ -351,6 +360,8 @@ class StorageImpl implements Storage {
this.api.setStorage(this.nativeId, this.storage);
}
setItem(key: string, value: string): void {
key = toStorageString(key);
value = toStorageString(value);
if (this.storage[this.prefix + key] === value)
return;
this.storage[this.prefix + key] = value;

View File

@@ -94,6 +94,8 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
super(app);
this.datastore = datastore;
this.app = app;
// ensure that all the users are loaded from the db.
this.usersService.getAllUsers();
this.pluginHosts.set('python', (_, pluginId, options) => new PythonRuntimeWorker(pluginId, options));
this.pluginHosts.set('node', (mainFilename, pluginId, options) => new NodeForkWorker(mainFilename, pluginId, options));

View File

@@ -212,7 +212,7 @@ async function start(mainFilename: string, options?: {
const sha = hash.digest().toString('hex');
if (checkHash === sha) {
const userToken = validateToken(tokenPart);
const userToken = checkValidUserToken(tokenPart);
if (userToken) {
res.locals.username = userToken.username;
res.locals.aclId = userToken.aclId;
@@ -420,19 +420,23 @@ async function start(mainFilename: string, options?: {
return req.secure ? 'login_user_token' : 'login_user_token_insecure';
};
const validateToken = (token: string) => {
const checkValidUserToken = (token: string) => {
if (!token)
return;
try {
return UserToken.validateToken(token);
const userToken = UserToken.validateToken(token);
if (scrypted.usersService.users.has(userToken.username))
return userToken;
}
catch (e) {
console.warn('invalid token', e.message);
// console.warn('invalid token', e.message);
}
}
const getSignedLoginUserTokenRawValue = (req: Request<any>) => req.signedCookies[getLoginUserToken(req)] as string;
const getSignedLoginUserToken = (req: Request<any>) => validateToken(getSignedLoginUserTokenRawValue(req));
const getSignedLoginUserToken = (req: Request<any>) => {
const token = req.signedCookies[getLoginUserToken(req)] as string;
return checkValidUserToken(token)
};
app.get('/logout', (req, res) => {
res.clearCookie(getLoginUserToken(req));
@@ -449,11 +453,7 @@ async function start(mainFilename: string, options?: {
if (process.env.SCRYPTED_ADMIN_USERNAME && process.env.SCRYPTED_ADMIN_TOKEN) {
let user = await db.tryGet(ScryptedUser, process.env.SCRYPTED_ADMIN_USERNAME);
if (!user) {
user = new ScryptedUser();
user._id = process.env.SCRYPTED_ADMIN_USERNAME;
setScryptedUserPassword(user, crypto.randomBytes(8).toString('hex'), Date.now());
user.token = crypto.randomBytes(16).toString('hex');
await db.upsert(user);
user = await scrypted.usersService.addUserInternal(process.env.SCRYPTED_ADMIN_USERNAME, crypto.randomBytes(8).toString('hex'), undefined);
hasLogin = true;
}
}
@@ -524,11 +524,7 @@ async function start(mainFilename: string, options?: {
return;
}
const user = new ScryptedUser();
user._id = username;
setScryptedUserPassword(user, password, timestamp);
user.token = crypto.randomBytes(16).toString('hex');
await db.upsert(user);
const user = await scrypted.usersService.addUserInternal(username, password, undefined);
hasLogin = true;
const userToken = new UserToken(username, user.aclId, timestamp);
@@ -621,10 +617,9 @@ async function start(mainFilename: string, options?: {
// cookie auth
try {
const login_user_token = getSignedLoginUserTokenRawValue(req);
if (!login_user_token)
const userToken = getSignedLoginUserToken(req);
if (!userToken)
throw new Error('Not logged in.');
const userToken = UserToken.validateToken(login_user_token);
res.send({
...createTokens(userToken),

View File

@@ -3,14 +3,32 @@ import { ScryptedRuntime } from "../runtime";
import crypto from 'crypto';
export class UsersService {
users = new Map<string, ScryptedUser>();
usersPromise: Promise<ScryptedUser[]>;
constructor(public scrypted: ScryptedRuntime) {
}
async getAllUsers() {
const users: ScryptedUser[] = [];
for await (const user of this.scrypted.datastore.getAll(ScryptedUser)) {
users.push(user);
private async ensureUsersPromise() {
if (!this.usersPromise) {
this.usersPromise = (async() => {
const users = new Map<string, ScryptedUser>();
for await (const user of this.scrypted.datastore.getAll(ScryptedUser)) {
users.set(user._id, user);
}
this.users = users;
return [...this.users.values()];
})();
}
return this.usersPromise;
}
private updateUsersPromise() {
this.usersPromise = Promise.resolve([...this.users.values()]);
}
async getAllUsers() {
const users = await this.ensureUsersPromise();
return users.map(user => ({
username: user._id,
@@ -19,19 +37,38 @@ export class UsersService {
}
async removeUser(username: string) {
await this.ensureUsersPromise();
await this.scrypted.datastore.removeId(ScryptedUser, username);
this.users.delete(username);
this.updateUsersPromise();
}
async removeAllUsers() {
await this.ensureUsersPromise();
await this.scrypted.datastore.removeAll(ScryptedUser);
this.users.clear();
this.updateUsersPromise();
}
async addUser(username: string, password: string, aclId: string) {
async addUserInternal(username: string, password: string, aclId: string) {
await this.ensureUsersPromise();
const user = new ScryptedUser();
user._id = username;
user.aclId = aclId;
user.token = crypto.randomBytes(16).toString('hex');
setScryptedUserPassword(user, password, Date.now());
await this.scrypted.datastore.upsert(user);
this.users.set(username, user);
this.updateUsersPromise();
return user;
}
async addUser(username: string, password: string, aclId: string) {
await this.addUserInternal(username, password, aclId);
}
}

View File

@@ -6,6 +6,9 @@ export class UserToken {
}
static validateToken(token: string): UserToken {
if (!token)
throw new Error('Token not found.');
let json: {
u: string,
a: string,