@scrypted/server 0.0.134 → 0.0.135
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @scrypted/server might be problematic. Click here for more details.
- package/.vscode/settings.json +1 -1
- package/dist/event-registry.js +1 -1
- package/dist/event-registry.js.map +1 -1
- package/dist/infer-defaults.js +1 -1
- package/dist/infer-defaults.js.map +1 -1
- package/dist/media-helpers.js +9 -25
- package/dist/media-helpers.js.map +1 -1
- package/dist/plugin/descriptor.js +1 -1
- package/dist/plugin/descriptor.js.map +1 -1
- package/dist/plugin/media.js +207 -101
- package/dist/plugin/media.js.map +1 -1
- package/dist/plugin/plugin-device.js +1 -1
- package/dist/plugin/plugin-device.js.map +1 -1
- package/dist/plugin/plugin-host-api.js +1 -1
- package/dist/plugin/plugin-host-api.js.map +1 -1
- package/dist/plugin/plugin-host.js +15 -6
- package/dist/plugin/plugin-host.js.map +1 -1
- package/dist/plugin/plugin-remote.js +1 -1
- package/dist/plugin/plugin-remote.js.map +1 -1
- package/dist/plugin/system.js +1 -1
- package/dist/plugin/system.js.map +1 -1
- package/dist/runtime.js +9 -3
- package/dist/runtime.js.map +1 -1
- package/dist/services/plugin.js +1 -1
- package/dist/services/plugin.js.map +1 -1
- package/dist/state.js +1 -1
- package/dist/state.js.map +1 -1
- package/package.json +5 -5
- package/python/plugin-remote.py +5 -5
- package/src/db-types.ts +1 -1
- package/src/event-registry.ts +1 -1
- package/src/http-interfaces.ts +1 -1
- package/src/infer-defaults.ts +1 -1
- package/src/media-helpers.ts +6 -27
- package/src/plugin/descriptor.ts +1 -1
- package/src/plugin/media.ts +232 -116
- package/src/plugin/plugin-api.ts +1 -1
- package/src/plugin/plugin-console.ts +1 -1
- package/src/plugin/plugin-device.ts +2 -2
- package/src/plugin/plugin-host-api.ts +1 -1
- package/src/plugin/plugin-host.ts +17 -7
- package/src/plugin/plugin-http.ts +1 -1
- package/src/plugin/plugin-lazy-remote.ts +1 -1
- package/src/plugin/plugin-remote-worker.ts +1 -1
- package/src/plugin/plugin-remote.ts +1 -1
- package/src/plugin/plugin-repl.ts +1 -1
- package/src/plugin/system.ts +1 -1
- package/src/runtime.ts +11 -3
- package/src/services/plugin.ts +1 -1
- package/src/state.ts +1 -1
- package/dist/convert.js +0 -117
- package/dist/convert.js.map +0 -1
- package/src/convert.ts +0 -122
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RpcPeer } from '../rpc';
|
|
2
2
|
import AdmZip from 'adm-zip';
|
|
3
|
-
import { Device, EngineIOHandler } from '@scrypted/
|
|
3
|
+
import { Device, EngineIOHandler } from '@scrypted/types'
|
|
4
4
|
import { ScryptedRuntime } from '../runtime';
|
|
5
5
|
import { Plugin } from '../db-types';
|
|
6
6
|
import io, { Socket } from 'engine.io';
|
|
@@ -27,6 +27,8 @@ import rimraf from 'rimraf';
|
|
|
27
27
|
|
|
28
28
|
export class PluginHost {
|
|
29
29
|
static sharedWorker: child_process.ChildProcess;
|
|
30
|
+
static sharedWorkerImmediateRestart = false;
|
|
31
|
+
|
|
30
32
|
worker: child_process.ChildProcess;
|
|
31
33
|
peer: RpcPeer;
|
|
32
34
|
pluginId: string;
|
|
@@ -51,6 +53,9 @@ export class PluginHost {
|
|
|
51
53
|
kill() {
|
|
52
54
|
this.killed = true;
|
|
53
55
|
this.api.removeListeners();
|
|
56
|
+
// things might get a bit race prone, so clear out the shared worker before killing.
|
|
57
|
+
if (this.worker === PluginHost.sharedWorker)
|
|
58
|
+
PluginHost.sharedWorker = undefined;
|
|
54
59
|
this.worker.kill('SIGKILL');
|
|
55
60
|
this.io.close();
|
|
56
61
|
for (const s of Object.values(this.ws)) {
|
|
@@ -254,7 +259,7 @@ export class PluginHost {
|
|
|
254
259
|
// stdin, stdout, stderr, peer in, peer out
|
|
255
260
|
stdio: ['pipe', 'pipe', 'pipe', 'pipe', 'pipe'],
|
|
256
261
|
env: Object.assign({
|
|
257
|
-
PYTHONPATH: path.join(process.cwd(), 'node_modules/@scrypted/
|
|
262
|
+
PYTHONPATH: path.join(process.cwd(), 'node_modules/@scrypted/types'),
|
|
258
263
|
}, process.env, env),
|
|
259
264
|
});
|
|
260
265
|
|
|
@@ -293,17 +298,22 @@ export class PluginHost {
|
|
|
293
298
|
Object.keys(this.packageJson.optionalDependencies || {}).length === 0;
|
|
294
299
|
if (useSharedWorker) {
|
|
295
300
|
if (!PluginHost.sharedWorker) {
|
|
296
|
-
|
|
301
|
+
const worker = child_process.fork(require.main.filename, ['child', '@scrypted/shared'], {
|
|
297
302
|
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
|
|
298
303
|
env: Object.assign({}, process.env, env),
|
|
299
304
|
serialization: 'advanced',
|
|
300
305
|
execArgv,
|
|
301
306
|
});
|
|
307
|
+
PluginHost.sharedWorker = worker;
|
|
302
308
|
PluginHost.sharedWorker.setMaxListeners(100);
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
309
|
+
const clearSharedWorker = () => {
|
|
310
|
+
if (worker === PluginHost.sharedWorker)
|
|
311
|
+
PluginHost.sharedWorker = undefined;
|
|
312
|
+
};
|
|
313
|
+
PluginHost.sharedWorker.on('close', () => clearSharedWorker);
|
|
314
|
+
PluginHost.sharedWorker.on('error', () => clearSharedWorker);
|
|
315
|
+
PluginHost.sharedWorker.on('exit', () => clearSharedWorker);
|
|
316
|
+
PluginHost.sharedWorker.on('disconnect', () => clearSharedWorker);
|
|
307
317
|
}
|
|
308
318
|
PluginHost.sharedWorker.send({
|
|
309
319
|
type: 'start',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Request, Response, Router } from 'express';
|
|
2
2
|
import bodyParser from 'body-parser';
|
|
3
|
-
import { HttpRequest } from '@scrypted/
|
|
3
|
+
import { HttpRequest } from '@scrypted/types';
|
|
4
4
|
import WebSocket, { Server as WebSocketServer } from "ws";
|
|
5
5
|
import { ServerResponse } from 'http';
|
|
6
6
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RpcMessage, RpcPeer } from '../rpc';
|
|
2
|
-
import { SystemManager, DeviceManager, ScryptedNativeId } from '@scrypted/
|
|
2
|
+
import { SystemManager, DeviceManager, ScryptedNativeId } from '@scrypted/types'
|
|
3
3
|
import { attachPluginRemote, PluginReader } from './plugin-remote';
|
|
4
4
|
import { PluginAPI } from './plugin-api';
|
|
5
5
|
import { MediaManagerImpl } from './media';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import AdmZip from 'adm-zip';
|
|
2
2
|
import { Volume } from 'memfs';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import { ScryptedNativeId, DeviceManager, Logger, Device, DeviceManifest, DeviceState, EndpointManager, SystemDeviceState, ScryptedStatic, SystemManager, MediaManager, ScryptedMimeTypes, ScryptedInterface, ScryptedInterfaceProperty, HttpRequest } from '@scrypted/
|
|
4
|
+
import { ScryptedNativeId, DeviceManager, Logger, Device, DeviceManifest, DeviceState, EndpointManager, SystemDeviceState, ScryptedStatic, SystemManager, MediaManager, ScryptedMimeTypes, ScryptedInterface, ScryptedInterfaceProperty, HttpRequest } from '@scrypted/types'
|
|
5
5
|
import { PluginAPI, PluginLogger, PluginRemote, PluginRemoteLoadZipOptions } from './plugin-api';
|
|
6
6
|
import { SystemManagerImpl } from './system';
|
|
7
7
|
import { RpcPeer, RPCResultError, PROPERTY_PROXY_ONEWAY_METHODS, PROPERTY_JSON_DISABLE_SERIALIZATION } from '../rpc';
|
|
@@ -2,7 +2,7 @@ import { listenZero } from './listen-zero';
|
|
|
2
2
|
import { Server } from 'net';
|
|
3
3
|
import { once } from 'events';
|
|
4
4
|
import repl from 'repl';
|
|
5
|
-
import { ScryptedStatic } from '@scrypted/
|
|
5
|
+
import { ScryptedStatic } from '@scrypted/types';
|
|
6
6
|
|
|
7
7
|
export async function createREPLServer(scrypted: ScryptedStatic, params: any, plugin: any): Promise<number> {
|
|
8
8
|
const { deviceManager, systemManager } = scrypted;
|
package/src/plugin/system.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EventListenerOptions, EventDetails, EventListenerRegister, ScryptedDevice, ScryptedInterface, ScryptedInterfaceDescriptors, SystemDeviceState, SystemManager, ScryptedInterfaceProperty, ScryptedDeviceType, Logger } from "@scrypted/
|
|
1
|
+
import { EventListenerOptions, EventDetails, EventListenerRegister, ScryptedDevice, ScryptedInterface, ScryptedInterfaceDescriptors, SystemDeviceState, SystemManager, ScryptedInterfaceProperty, ScryptedDeviceType, Logger } from "@scrypted/types";
|
|
2
2
|
import { PluginAPI } from "./plugin-api";
|
|
3
3
|
import { handleFunctionInvocations, PrimitiveProxyHandler, PROPERTY_PROXY_ONEWAY_METHODS } from '../rpc';
|
|
4
4
|
import { EventRegistry } from "../event-registry";
|
package/src/runtime.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Level } from './level';
|
|
2
2
|
import { PluginHost } from './plugin/plugin-host';
|
|
3
|
-
import { ScryptedNativeId, Device, EngineIOHandler, HttpRequest, HttpRequestHandler, OauthClient, PushHandler, ScryptedDevice, ScryptedInterface, ScryptedInterfaceProperty } from '@scrypted/
|
|
3
|
+
import { ScryptedNativeId, Device, EngineIOHandler, HttpRequest, HttpRequestHandler, OauthClient, PushHandler, ScryptedDevice, ScryptedInterface, ScryptedInterfaceProperty } from '@scrypted/types';
|
|
4
4
|
import { PluginDeviceProxyHandler } from './plugin/plugin-device';
|
|
5
5
|
import { Plugin, PluginDevice, ScryptedAlert } from './db-types';
|
|
6
6
|
import { getState, ScryptedStateManager, setState } from './state';
|
|
@@ -353,6 +353,11 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
|
353
353
|
const existing = this.plugins[pluginId];
|
|
354
354
|
if (existing) {
|
|
355
355
|
delete this.plugins[pluginId];
|
|
356
|
+
|
|
357
|
+
if (existing.worker === PluginHost.sharedWorker) {
|
|
358
|
+
PluginHost.sharedWorkerImmediateRestart = true;
|
|
359
|
+
setTimeout(() => PluginHost.sharedWorkerImmediateRestart = false, 10000);
|
|
360
|
+
}
|
|
356
361
|
existing.kill();
|
|
357
362
|
}
|
|
358
363
|
}
|
|
@@ -491,11 +496,14 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
|
491
496
|
}
|
|
492
497
|
|
|
493
498
|
setupPluginHostAutoRestart(pluginHost: PluginHost) {
|
|
499
|
+
const usingSharedWorker = pluginHost.worker === PluginHost.sharedWorker;
|
|
500
|
+
|
|
494
501
|
pluginHost.worker.once('exit', () => {
|
|
495
502
|
if (pluginHost.killed)
|
|
496
503
|
return;
|
|
497
504
|
pluginHost.kill();
|
|
498
|
-
|
|
505
|
+
const timeout = usingSharedWorker && PluginHost.sharedWorkerImmediateRestart ? 0 : 60000;
|
|
506
|
+
console.error(`plugin unexpectedly exited, restarting in ${timeout}ms`, pluginHost.pluginId);
|
|
499
507
|
setTimeout(async () => {
|
|
500
508
|
const existing = this.plugins[pluginHost.pluginId];
|
|
501
509
|
if (existing !== pluginHost) {
|
|
@@ -515,7 +523,7 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
|
515
523
|
catch (e) {
|
|
516
524
|
console.error('error restarting plugin', plugin._id, e);
|
|
517
525
|
}
|
|
518
|
-
},
|
|
526
|
+
}, timeout);
|
|
519
527
|
});
|
|
520
528
|
}
|
|
521
529
|
|
package/src/services/plugin.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ScryptedInterfaceProperty, ScryptedNativeId } from "@scrypted/
|
|
1
|
+
import { ScryptedInterfaceProperty, ScryptedNativeId } from "@scrypted/types";
|
|
2
2
|
import { ScryptedRuntime } from "../runtime";
|
|
3
3
|
import { Plugin } from '../db-types';
|
|
4
4
|
import { getState } from "../state";
|
package/src/state.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ScryptedRuntime } from "./runtime";
|
|
2
|
-
import { ScryptedNativeId, EventDetails, EventListenerOptions, EventListenerRegister, Refresh, ScryptedInterface, ScryptedInterfaceProperty, SystemDeviceState } from "@scrypted/
|
|
2
|
+
import { ScryptedNativeId, EventDetails, EventListenerOptions, EventListenerRegister, Refresh, ScryptedInterface, ScryptedInterfaceProperty, SystemDeviceState } from "@scrypted/types";
|
|
3
3
|
import { RefreshSymbol } from "./plugin/plugin-device";
|
|
4
4
|
import throttle from 'lodash/throttle';
|
|
5
5
|
import { sleep } from "./sleep";
|
package/dist/convert.js
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.convert = exports.ensureBuffer = void 0;
|
|
7
|
-
const types_1 = require("@scrypted/sdk/types");
|
|
8
|
-
const node_dijkstra_1 = __importDefault(require("node-dijkstra"));
|
|
9
|
-
const whatwg_mimetype_1 = __importDefault(require("whatwg-mimetype"));
|
|
10
|
-
const axios_1 = __importDefault(require("axios"));
|
|
11
|
-
const https_1 = __importDefault(require("https"));
|
|
12
|
-
function typeMatches(target, candidate) {
|
|
13
|
-
// candidate will accept anything
|
|
14
|
-
if (candidate === '*')
|
|
15
|
-
return true;
|
|
16
|
-
return target === candidate;
|
|
17
|
-
}
|
|
18
|
-
function mimeMatches(target, candidate) {
|
|
19
|
-
return typeMatches(target.type, candidate.type) && typeMatches(target.subtype, candidate.subtype);
|
|
20
|
-
}
|
|
21
|
-
const httpsAgent = new https_1.default.Agent({
|
|
22
|
-
rejectUnauthorized: false
|
|
23
|
-
});
|
|
24
|
-
async function ensureBuffer(data) {
|
|
25
|
-
if (typeof data === 'string') {
|
|
26
|
-
const ab = await axios_1.default.get(data, {
|
|
27
|
-
responseType: 'arraybuffer',
|
|
28
|
-
httpsAgent,
|
|
29
|
-
});
|
|
30
|
-
return Buffer.from(ab.data);
|
|
31
|
-
}
|
|
32
|
-
return Buffer.from(data);
|
|
33
|
-
}
|
|
34
|
-
exports.ensureBuffer = ensureBuffer;
|
|
35
|
-
async function convert(converters, mediaObject, toMimeType) {
|
|
36
|
-
// console.log('converting', mediaObject.mimeType, toMimeType);
|
|
37
|
-
const mediaMime = new whatwg_mimetype_1.default(mediaObject.mimeType);
|
|
38
|
-
const outputMime = new whatwg_mimetype_1.default(toMimeType);
|
|
39
|
-
if (mimeMatches(mediaMime, outputMime)) {
|
|
40
|
-
return {
|
|
41
|
-
mimeType: outputMime.essence,
|
|
42
|
-
data: await mediaObject.getData(),
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
const converterIds = new Map();
|
|
46
|
-
const converterReverseids = new Map();
|
|
47
|
-
let id = 0;
|
|
48
|
-
for (const converter of converters) {
|
|
49
|
-
const cid = (id++).toString();
|
|
50
|
-
converterIds.set(converter, cid);
|
|
51
|
-
converterReverseids.set(cid, converter);
|
|
52
|
-
}
|
|
53
|
-
const nodes = {};
|
|
54
|
-
const mediaNode = {};
|
|
55
|
-
nodes['mediaObject'] = mediaNode;
|
|
56
|
-
nodes['output'] = {};
|
|
57
|
-
for (const converter of converters) {
|
|
58
|
-
try {
|
|
59
|
-
const inputMime = new whatwg_mimetype_1.default(converter.fromMimeType);
|
|
60
|
-
const convertedMime = new whatwg_mimetype_1.default(converter.toMimeType);
|
|
61
|
-
const targetId = converterIds.get(converter);
|
|
62
|
-
const node = nodes[targetId] = {};
|
|
63
|
-
for (const candidate of converters) {
|
|
64
|
-
try {
|
|
65
|
-
const candidateMime = new whatwg_mimetype_1.default(candidate.fromMimeType);
|
|
66
|
-
if (!mimeMatches(convertedMime, candidateMime))
|
|
67
|
-
continue;
|
|
68
|
-
const candidateId = converterIds.get(candidate);
|
|
69
|
-
node[candidateId] = 1;
|
|
70
|
-
}
|
|
71
|
-
catch (e) {
|
|
72
|
-
console.warn('skipping converter due to error', e);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (mimeMatches(mediaMime, inputMime)) {
|
|
76
|
-
mediaNode[targetId] = 1;
|
|
77
|
-
}
|
|
78
|
-
if (mimeMatches(convertedMime, outputMime)) {
|
|
79
|
-
node['output'] = 1;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
catch (e) {
|
|
83
|
-
console.warn('skipping converter due to error', e);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
const graph = new node_dijkstra_1.default();
|
|
87
|
-
for (const id of Object.keys(nodes)) {
|
|
88
|
-
graph.addNode(id, nodes[id]);
|
|
89
|
-
}
|
|
90
|
-
const route = graph.path('mediaObject', 'output');
|
|
91
|
-
if (!route || !route.length)
|
|
92
|
-
throw new Error('no converter found');
|
|
93
|
-
// pop off the mediaObject start node, no conversion necessary.
|
|
94
|
-
route.shift();
|
|
95
|
-
// also remove the output node.
|
|
96
|
-
route.splice(route.length - 1);
|
|
97
|
-
let value = await mediaObject.getData();
|
|
98
|
-
let valueMime = new whatwg_mimetype_1.default(mediaObject.mimeType);
|
|
99
|
-
for (const node of route) {
|
|
100
|
-
const converter = converterReverseids.get(node);
|
|
101
|
-
const targetMime = new whatwg_mimetype_1.default(converter.toMimeType);
|
|
102
|
-
const inputMime = new whatwg_mimetype_1.default(converter.fromMimeType);
|
|
103
|
-
if (typeof value === 'string' && !inputMime.parameters.has(types_1.ScryptedMimeTypes.AcceptUrlParameter)) {
|
|
104
|
-
value = await ensureBuffer(value);
|
|
105
|
-
}
|
|
106
|
-
value = await converter.convert(value, valueMime.essence);
|
|
107
|
-
const type = targetMime.type === '*' ? valueMime.type : targetMime.type;
|
|
108
|
-
const subtype = targetMime.subtype === '*' ? valueMime.subtype : targetMime.subtype;
|
|
109
|
-
valueMime = new whatwg_mimetype_1.default(`${type}/${subtype}`);
|
|
110
|
-
}
|
|
111
|
-
return {
|
|
112
|
-
data: value,
|
|
113
|
-
mimeType: valueMime.essence,
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
exports.convert = convert;
|
|
117
|
-
//# sourceMappingURL=convert.js.map
|
package/dist/convert.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"convert.js","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":";;;;;;AAAA,+CAAyE;AACzE,kEAAkC;AAClC,sEAAuC;AAEvC,kDAA0B;AAC1B,kDAA0B;AAE1B,SAAS,WAAW,CAAC,MAAc,EAAE,SAAiB;IAClD,iCAAiC;IACjC,IAAI,SAAS,KAAK,GAAG;QACjB,OAAO,IAAI,CAAC;IAChB,OAAO,MAAM,KAAK,SAAS,CAAC;AAChC,CAAC;AAED,SAAS,WAAW,CAAC,MAAgB,EAAE,SAAmB;IACtD,OAAO,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;AACtG,CAAC;AAED,MAAM,UAAU,GAAG,IAAI,eAAK,CAAC,KAAK,CAAC;IAC/B,kBAAkB,EAAE,KAAK;CAC5B,CAAC,CAAA;AAEK,KAAK,UAAU,YAAY,CAAC,IAAqB;IACpD,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;QAC1B,MAAM,EAAE,GAAG,MAAM,eAAK,CAAC,GAAG,CAAC,IAAc,EAAE;YACvC,YAAY,EAAE,aAAa;YAC3B,UAAU;SACb,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;KAC/B;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC;AATD,oCASC;AAEM,KAAK,UAAU,OAAO,CAAC,UAA6B,EAAE,WAA8B,EAAE,UAAkB;IAC3G,+DAA+D;IAC/D,MAAM,SAAS,GAAG,IAAI,yBAAQ,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,IAAI,yBAAQ,CAAC,UAAU,CAAC,CAAC;IAE5C,IAAI,WAAW,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE;QACpC,OAAO;YACH,QAAQ,EAAE,UAAU,CAAC,OAAO;YAC5B,IAAI,EAAE,MAAM,WAAW,CAAC,OAAO,EAAE;SACpC,CAAA;KACJ;IAED,MAAM,YAAY,GAAG,IAAI,GAAG,EAA2B,CAAC;IACxD,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAA2B,CAAC;IAC/D,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE;QAChC,MAAM,GAAG,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC9B,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QACjC,mBAAmB,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;KAC3C;IAED,MAAM,KAAK,GAAQ,EAAE,CAAC;IACtB,MAAM,SAAS,GAAQ,EAAE,CAAC;IAC1B,KAAK,CAAC,aAAa,CAAC,GAAG,SAAS,CAAC;IACjC,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;IACrB,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE;QAChC,IAAI;YACA,MAAM,SAAS,GAAG,IAAI,yBAAQ,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;YACvD,MAAM,aAAa,GAAG,IAAI,yBAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;YACzD,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAC7C,MAAM,IAAI,GAAQ,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;YACvC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE;gBAChC,IAAI;oBACA,MAAM,aAAa,GAAG,IAAI,yBAAQ,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;oBAC3D,IAAI,CAAC,WAAW,CAAC,aAAa,EAAE,aAAa,CAAC;wBAC1C,SAAS;oBACb,MAAM,WAAW,GAAG,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;oBAChD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;iBACzB;gBACD,OAAO,CAAC,EAAE;oBACN,OAAO,CAAC,IAAI,CAAC,iCAAiC,EAAE,CAAC,CAAC,CAAA;iBACrD;aACJ;YAED,IAAI,WAAW,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE;gBACnC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;aAC3B;YACD,IAAI,WAAW,CAAC,aAAa,EAAE,UAAU,CAAC,EAAE;gBACxC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;aACtB;SACJ;QACD,OAAO,CAAC,EAAE;YACN,OAAO,CAAC,IAAI,CAAC,iCAAiC,EAAE,CAAC,CAAC,CAAA;SACrD;KACJ;IAED,MAAM,KAAK,GAAG,IAAI,uBAAK,EAAE,CAAC;IAC1B,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;QACjC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;KAChC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAkB,CAAC;IACnE,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM;QACvB,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAC1C,+DAA+D;IAC/D,KAAK,CAAC,KAAK,EAAE,CAAC;IACd,+BAA+B;IAC/B,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/B,IAAI,KAAK,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE,CAAC;IACxC,IAAI,SAAS,GAAG,IAAI,yBAAQ,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IACnD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;QACtB,MAAM,SAAS,GAAG,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChD,MAAM,UAAU,GAAG,IAAI,yBAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACtD,MAAM,SAAS,GAAG,IAAI,yBAAQ,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QAEvD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,yBAAiB,CAAC,kBAAkB,CAAC,EAAE;YAC9F,KAAK,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,CAAC;SACrC;QACD,KAAK,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;QACxE,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC;QACpF,SAAS,GAAG,IAAI,yBAAQ,CAAC,GAAG,IAAI,IAAI,OAAO,EAAE,CAAC,CAAC;KAClD;IAED,OAAO;QACH,IAAI,EAAE,KAAK;QACX,QAAQ,EAAE,SAAS,CAAC,OAAO;KAC9B,CAAC;AACN,CAAC;AAxFD,0BAwFC"}
|
package/src/convert.ts
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { BufferConverter, ScryptedMimeTypes } from '@scrypted/sdk/types';
|
|
2
|
-
import Graph from 'node-dijkstra';
|
|
3
|
-
import MimeType from 'whatwg-mimetype';
|
|
4
|
-
import { MediaObjectRemote } from './plugin/plugin-api';
|
|
5
|
-
import axios from 'axios';
|
|
6
|
-
import https from 'https';
|
|
7
|
-
|
|
8
|
-
function typeMatches(target: string, candidate: string): boolean {
|
|
9
|
-
// candidate will accept anything
|
|
10
|
-
if (candidate === '*')
|
|
11
|
-
return true;
|
|
12
|
-
return target === candidate;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function mimeMatches(target: MimeType, candidate: MimeType) {
|
|
16
|
-
return typeMatches(target.type, candidate.type) && typeMatches(target.subtype, candidate.subtype);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const httpsAgent = new https.Agent({
|
|
20
|
-
rejectUnauthorized: false
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
export async function ensureBuffer(data: Buffer | string): Promise<Buffer> {
|
|
24
|
-
if (typeof data === 'string') {
|
|
25
|
-
const ab = await axios.get(data as string, {
|
|
26
|
-
responseType: 'arraybuffer',
|
|
27
|
-
httpsAgent,
|
|
28
|
-
});
|
|
29
|
-
return Buffer.from(ab.data);
|
|
30
|
-
}
|
|
31
|
-
return Buffer.from(data);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export async function convert(converters: BufferConverter[], mediaObject: MediaObjectRemote, toMimeType: string): Promise<{ data: Buffer | string, mimeType: string }> {
|
|
35
|
-
// console.log('converting', mediaObject.mimeType, toMimeType);
|
|
36
|
-
const mediaMime = new MimeType(mediaObject.mimeType);
|
|
37
|
-
const outputMime = new MimeType(toMimeType);
|
|
38
|
-
|
|
39
|
-
if (mimeMatches(mediaMime, outputMime)) {
|
|
40
|
-
return {
|
|
41
|
-
mimeType: outputMime.essence,
|
|
42
|
-
data: await mediaObject.getData(),
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const converterIds = new Map<BufferConverter, string>();
|
|
47
|
-
const converterReverseids = new Map<string, BufferConverter>();
|
|
48
|
-
let id = 0;
|
|
49
|
-
for (const converter of converters) {
|
|
50
|
-
const cid = (id++).toString();
|
|
51
|
-
converterIds.set(converter, cid);
|
|
52
|
-
converterReverseids.set(cid, converter);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const nodes: any = {};
|
|
56
|
-
const mediaNode: any = {};
|
|
57
|
-
nodes['mediaObject'] = mediaNode;
|
|
58
|
-
nodes['output'] = {};
|
|
59
|
-
for (const converter of converters) {
|
|
60
|
-
try {
|
|
61
|
-
const inputMime = new MimeType(converter.fromMimeType);
|
|
62
|
-
const convertedMime = new MimeType(converter.toMimeType);
|
|
63
|
-
const targetId = converterIds.get(converter);
|
|
64
|
-
const node: any = nodes[targetId] = {};
|
|
65
|
-
for (const candidate of converters) {
|
|
66
|
-
try {
|
|
67
|
-
const candidateMime = new MimeType(candidate.fromMimeType);
|
|
68
|
-
if (!mimeMatches(convertedMime, candidateMime))
|
|
69
|
-
continue;
|
|
70
|
-
const candidateId = converterIds.get(candidate);
|
|
71
|
-
node[candidateId] = 1;
|
|
72
|
-
}
|
|
73
|
-
catch (e) {
|
|
74
|
-
console.warn('skipping converter due to error', e)
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (mimeMatches(mediaMime, inputMime)) {
|
|
79
|
-
mediaNode[targetId] = 1;
|
|
80
|
-
}
|
|
81
|
-
if (mimeMatches(convertedMime, outputMime)) {
|
|
82
|
-
node['output'] = 1;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
catch (e) {
|
|
86
|
-
console.warn('skipping converter due to error', e)
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const graph = new Graph();
|
|
91
|
-
for (const id of Object.keys(nodes)) {
|
|
92
|
-
graph.addNode(id, nodes[id]);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const route = graph.path('mediaObject', 'output') as Array<string>;
|
|
96
|
-
if (!route || !route.length)
|
|
97
|
-
throw new Error('no converter found');
|
|
98
|
-
// pop off the mediaObject start node, no conversion necessary.
|
|
99
|
-
route.shift();
|
|
100
|
-
// also remove the output node.
|
|
101
|
-
route.splice(route.length - 1);
|
|
102
|
-
let value = await mediaObject.getData();
|
|
103
|
-
let valueMime = new MimeType(mediaObject.mimeType);
|
|
104
|
-
for (const node of route) {
|
|
105
|
-
const converter = converterReverseids.get(node);
|
|
106
|
-
const targetMime = new MimeType(converter.toMimeType);
|
|
107
|
-
const inputMime = new MimeType(converter.fromMimeType);
|
|
108
|
-
|
|
109
|
-
if (typeof value === 'string' && !inputMime.parameters.has(ScryptedMimeTypes.AcceptUrlParameter)) {
|
|
110
|
-
value = await ensureBuffer(value);
|
|
111
|
-
}
|
|
112
|
-
value = await converter.convert(value, valueMime.essence);
|
|
113
|
-
const type = targetMime.type === '*' ? valueMime.type : targetMime.type;
|
|
114
|
-
const subtype = targetMime.subtype === '*' ? valueMime.subtype : targetMime.subtype;
|
|
115
|
-
valueMime = new MimeType(`${type}/${subtype}`);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
data: value,
|
|
120
|
-
mimeType: valueMime.essence,
|
|
121
|
-
};
|
|
122
|
-
}
|