@scrypted/server 0.0.133 → 0.0.138
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 +5 -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 +10 -3
- 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 +36 -8
- package/dist/runtime.js.map +1 -1
- package/dist/services/plugin.js +2 -10
- package/dist/services/plugin.js.map +1 -1
- package/dist/state.js +1 -1
- package/dist/state.js.map +1 -1
- package/package.json +7 -6
- 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 +7 -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 +12 -4
- package/src/plugin/plugin-repl.ts +1 -1
- package/src/plugin/system.ts +1 -1
- package/src/runtime.ts +42 -10
- package/src/services/plugin.ts +2 -10
- 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
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';
|
|
@@ -313,6 +313,18 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
+
async getPackageJson(pluginId: string) {
|
|
317
|
+
let packageJson;
|
|
318
|
+
if (this.plugins[pluginId]) {
|
|
319
|
+
packageJson = this.plugins[pluginId].packageJson;
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
const plugin = await this.datastore.tryGet(Plugin, pluginId);
|
|
323
|
+
packageJson = plugin.packageJson;
|
|
324
|
+
}
|
|
325
|
+
return packageJson;
|
|
326
|
+
}
|
|
327
|
+
|
|
316
328
|
handleEngineIOEndpoint(req: Request, res: ServerResponse, endpointRequest: HttpRequest, pluginData: HttpPluginData) {
|
|
317
329
|
const { pluginHost, pluginDevice } = pluginData;
|
|
318
330
|
|
|
@@ -341,6 +353,11 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
|
341
353
|
const existing = this.plugins[pluginId];
|
|
342
354
|
if (existing) {
|
|
343
355
|
delete this.plugins[pluginId];
|
|
356
|
+
|
|
357
|
+
if (existing.worker === PluginHost.sharedWorker) {
|
|
358
|
+
PluginHost.sharedWorkerImmediateRestart = true;
|
|
359
|
+
setTimeout(() => PluginHost.sharedWorkerImmediateRestart = false, 10000);
|
|
360
|
+
}
|
|
344
361
|
existing.kill();
|
|
345
362
|
}
|
|
346
363
|
}
|
|
@@ -479,11 +496,14 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
|
479
496
|
}
|
|
480
497
|
|
|
481
498
|
setupPluginHostAutoRestart(pluginHost: PluginHost) {
|
|
499
|
+
const usingSharedWorker = pluginHost.worker === PluginHost.sharedWorker;
|
|
500
|
+
|
|
482
501
|
pluginHost.worker.once('exit', () => {
|
|
483
502
|
if (pluginHost.killed)
|
|
484
503
|
return;
|
|
485
504
|
pluginHost.kill();
|
|
486
|
-
|
|
505
|
+
const timeout = usingSharedWorker && PluginHost.sharedWorkerImmediateRestart ? 0 : 60000;
|
|
506
|
+
console.error(`plugin unexpectedly exited, restarting in ${timeout}ms`, pluginHost.pluginId);
|
|
487
507
|
setTimeout(async () => {
|
|
488
508
|
const existing = this.plugins[pluginHost.pluginId];
|
|
489
509
|
if (existing !== pluginHost) {
|
|
@@ -503,7 +523,7 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
|
503
523
|
catch (e) {
|
|
504
524
|
console.error('error restarting plugin', plugin._id, e);
|
|
505
525
|
}
|
|
506
|
-
},
|
|
526
|
+
}, timeout);
|
|
507
527
|
});
|
|
508
528
|
}
|
|
509
529
|
|
|
@@ -621,7 +641,12 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
|
621
641
|
const providedRoom = device.room;
|
|
622
642
|
const isUsingDefaultRoom = getDisplayRoom(pluginDevice) === getProvidedRoomOrDefault(pluginDevice);
|
|
623
643
|
|
|
624
|
-
|
|
644
|
+
let providedInterfaces = device.interfaces.slice();
|
|
645
|
+
if (!device.nativeId)
|
|
646
|
+
providedInterfaces.push(ScryptedInterface.ScryptedPlugin);
|
|
647
|
+
else
|
|
648
|
+
providedInterfaces = providedInterfaces.filter(iface => iface !== ScryptedInterface.ScryptedPlugin);
|
|
649
|
+
providedInterfaces = PluginDeviceProxyHandler.sortInterfaces(providedInterfaces);
|
|
625
650
|
// assure final mixin resolved interface list has at least all the
|
|
626
651
|
// interfaces from the provided. the actual list will resolve lazily.
|
|
627
652
|
let mixinInterfaces: string[] = [];
|
|
@@ -671,10 +696,6 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
|
671
696
|
return ret;
|
|
672
697
|
}
|
|
673
698
|
|
|
674
|
-
async migrate(pluginDevice: PluginDevice) {
|
|
675
|
-
// nothing right now.
|
|
676
|
-
}
|
|
677
|
-
|
|
678
699
|
killall() {
|
|
679
700
|
for (const host of Object.values(this.plugins)) {
|
|
680
701
|
host?.kill();
|
|
@@ -689,8 +710,6 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
|
689
710
|
process.on('SIGTERM', () => this.killall());
|
|
690
711
|
|
|
691
712
|
for await (const pluginDevice of this.datastore.getAll(PluginDevice)) {
|
|
692
|
-
this.migrate(pluginDevice);
|
|
693
|
-
|
|
694
713
|
// this may happen due to race condition around deletion/update. investigate.
|
|
695
714
|
if (!pluginDevice.state) {
|
|
696
715
|
this.datastore.remove(pluginDevice);
|
|
@@ -699,8 +718,21 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
|
699
718
|
|
|
700
719
|
this.pluginDevices[pluginDevice._id] = pluginDevice;
|
|
701
720
|
let mixins: string[] = getState(pluginDevice, ScryptedInterfaceProperty.mixins) || [];
|
|
721
|
+
|
|
722
|
+
let dirty = false;
|
|
702
723
|
if (mixins.includes(null) || mixins.includes(undefined)) {
|
|
724
|
+
dirty = true;
|
|
703
725
|
setState(pluginDevice, ScryptedInterfaceProperty.mixins, mixins.filter(e => !!e));
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
const interfaces: string[] = getState(pluginDevice, ScryptedInterfaceProperty.providedInterfaces);
|
|
729
|
+
if (!pluginDevice.nativeId && !interfaces.includes(ScryptedInterface.ScryptedPlugin)) {
|
|
730
|
+
dirty = true;
|
|
731
|
+
interfaces.push(ScryptedInterface.ScryptedPlugin);
|
|
732
|
+
setState(pluginDevice, ScryptedInterfaceProperty.providedInterfaces, PluginDeviceProxyHandler.sortInterfaces(interfaces));
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
if (dirty) {
|
|
704
736
|
this.datastore.upsert(pluginDevice);
|
|
705
737
|
}
|
|
706
738
|
}
|
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";
|
|
@@ -62,15 +62,7 @@ export class PluginComponent {
|
|
|
62
62
|
return this.scrypted.plugins[pluginId]?.kill();
|
|
63
63
|
}
|
|
64
64
|
async getPackageJson(pluginId: string) {
|
|
65
|
-
|
|
66
|
-
if (this.scrypted.plugins[pluginId]) {
|
|
67
|
-
packageJson = this.scrypted.plugins[pluginId].packageJson;
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
const plugin = await this.scrypted.datastore.tryGet(Plugin, pluginId);
|
|
71
|
-
packageJson = plugin.packageJson;
|
|
72
|
-
}
|
|
73
|
-
return packageJson;
|
|
65
|
+
return this.scrypted.getPackageJson(pluginId);
|
|
74
66
|
}
|
|
75
67
|
async getDeviceInfo(id: string) {
|
|
76
68
|
const pluginDevice = this.scrypted.findPluginDeviceById(id);
|
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":"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"}
|
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
|
-
}
|