base64 support, ApiJson, Eval string inputs
This commit is contained in:
145
src/nodes/ApiJson.ts
Normal file
145
src/nodes/ApiJson.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import {
|
||||
NumberVar,
|
||||
StringVar,
|
||||
BooleanVar,
|
||||
} from './io/AbstractIOSet';
|
||||
import Node from './Node';
|
||||
import _ from 'lodash';
|
||||
|
||||
const inputs = {
|
||||
propertyPath0: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
propertyPath1: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
propertyPath2: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
propertyPath3: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
propertyPath4: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
propertyPath5: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
propertyPath6: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
propertyPath7: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
url: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
payload: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
method: {
|
||||
type: 'String',
|
||||
default: 'GET',
|
||||
enum: ['GET', 'POST', 'PUT', 'DELETE'],
|
||||
} as StringVar,
|
||||
throttleMs: {
|
||||
type: 'Number',
|
||||
default: 10000,
|
||||
} as NumberVar,
|
||||
compress: {
|
||||
type: 'Boolean',
|
||||
default: false,
|
||||
} as BooleanVar,
|
||||
quality: {
|
||||
type: 'Number',
|
||||
default: 95,
|
||||
min: 0,
|
||||
max: 100,
|
||||
} as NumberVar,
|
||||
};
|
||||
|
||||
const outputs = {
|
||||
value0: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
value1: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
value2: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
value3: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
value4: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
value5: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
value6: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
value7: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
};
|
||||
|
||||
export default class ApiJson extends Node<typeof inputs, typeof outputs> {
|
||||
private fetchDataThrottled;
|
||||
private throttleWaitMs;
|
||||
|
||||
constructor(options = {}) {
|
||||
super('ApiJson', inputs, outputs);
|
||||
this.throttleWaitMs = this.in.throttleMs.value;
|
||||
this.fetchDataThrottled = _.throttle(this.fetchData, this.throttleWaitMs);
|
||||
}
|
||||
|
||||
async _update() {
|
||||
const url = this.in.url.value;
|
||||
|
||||
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.throttleWaitMs !== this.in.throttleMs.value) {
|
||||
this.throttleWaitMs = this.in.throttleMs.value;
|
||||
this.fetchDataThrottled = _.throttle(this.fetchData, this.throttleWaitMs);
|
||||
}
|
||||
|
||||
let data: object;
|
||||
try {
|
||||
data = await this.fetchDataThrottled();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
await new Promise((resolve) => setTimeout(resolve, this.throttleWaitMs));
|
||||
return;
|
||||
}
|
||||
|
||||
this.out.value0.value = _.get(data, this.in.propertyPath0.value);
|
||||
this.out.value1.value = _.get(data, this.in.propertyPath1.value);
|
||||
this.out.value2.value = _.get(data, this.in.propertyPath2.value);
|
||||
this.out.value3.value = _.get(data, this.in.propertyPath3.value);
|
||||
this.out.value4.value = _.get(data, this.in.propertyPath4.value);
|
||||
this.out.value5.value = _.get(data, this.in.propertyPath5.value);
|
||||
this.out.value6.value = _.get(data, this.in.propertyPath6.value);
|
||||
this.out.value7.value = _.get(data, this.in.propertyPath7.value);
|
||||
}
|
||||
|
||||
async fetchData() {
|
||||
const response = await fetch(this.in.url.value, {
|
||||
method: this.in.method.value,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
...(this.in.payload.value ? {body: this.in.payload.value} : {}),
|
||||
});
|
||||
|
||||
if (response.status >= 400) {
|
||||
throw new Error(response.statusText);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
}
|
||||
84
src/nodes/Base64ToImg.ts
Normal file
84
src/nodes/Base64ToImg.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import {
|
||||
ImageVar,
|
||||
NumberVar,
|
||||
StringVar,
|
||||
} from './io/AbstractIOSet';
|
||||
import Node from './Node';
|
||||
import {canvasPool2D} from '../canvas/CanvasPool';
|
||||
|
||||
const inputs = {
|
||||
base64: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
};
|
||||
|
||||
const outputs = {
|
||||
image: {
|
||||
type: 'Image',
|
||||
} as ImageVar,
|
||||
width: {
|
||||
type: 'Number',
|
||||
} as NumberVar,
|
||||
height: {
|
||||
type: 'Number',
|
||||
} as NumberVar,
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: after successful tests move this to the library
|
||||
*/
|
||||
export default class Base64ToImg extends Node<typeof inputs, typeof outputs> {
|
||||
constructor(options = {}) {
|
||||
super('Base64ToImg', inputs, outputs);
|
||||
}
|
||||
|
||||
async _update() {
|
||||
let base64String = this.in.base64.value;
|
||||
|
||||
if (!base64String) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!base64String.startsWith('data:image/')) {
|
||||
base64String = `data:${this.estimateImageMimeType(base64String)};base64,${base64String}`;
|
||||
}
|
||||
|
||||
const canvas = canvasPool2D.createCanvas();
|
||||
canvas.acquire();
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
const canvasContext = canvas.getContext('2d');
|
||||
canvasContext.drawImage(img, 0, 0);
|
||||
resolve(img);
|
||||
};
|
||||
|
||||
img.onerror = (e) => {
|
||||
reject(new Error(e as string));
|
||||
};
|
||||
|
||||
img.src = base64String;
|
||||
});
|
||||
|
||||
this.out.image.value = canvas;
|
||||
this.out.width.value = canvas.width;
|
||||
this.out.height.value = canvas.height;
|
||||
}
|
||||
|
||||
private estimateImageMimeType(base64String: string): string {
|
||||
// Attempt to estimate the MIME type based on base64 signature.
|
||||
if (base64String.startsWith('iVBORw0KGgo=')) {
|
||||
return 'image/png';
|
||||
} else if (base64String.startsWith('/9j/')) {
|
||||
return 'image/jpeg';
|
||||
} else if (base64String.startsWith('UklGR')) {
|
||||
return 'image/webp';
|
||||
} else {
|
||||
// Default or unknown type
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,11 @@ import {
|
||||
StringVar,
|
||||
BooleanVar,
|
||||
} from './io/AbstractIOSet';
|
||||
import get from 'lodash/get';
|
||||
import _ from 'lodash';
|
||||
|
||||
const inputs = {
|
||||
image: {
|
||||
type: 'Image'
|
||||
type: 'Image',
|
||||
} as ImageVar,
|
||||
i0: {
|
||||
type: 'Number',
|
||||
@@ -43,6 +43,38 @@ const inputs = {
|
||||
type: 'Number',
|
||||
default: 0,
|
||||
} as NumberVar,
|
||||
i8: {
|
||||
type: 'String',
|
||||
default: undefined,
|
||||
} as StringVar,
|
||||
i9: {
|
||||
type: 'String',
|
||||
default: undefined,
|
||||
} as StringVar,
|
||||
i10: {
|
||||
type: 'String',
|
||||
default: undefined,
|
||||
} as StringVar,
|
||||
i11: {
|
||||
type: 'String',
|
||||
default: undefined,
|
||||
} as StringVar,
|
||||
i12: {
|
||||
type: 'String',
|
||||
default: undefined,
|
||||
} as StringVar,
|
||||
i13: {
|
||||
type: 'String',
|
||||
default: undefined,
|
||||
} as StringVar,
|
||||
i14: {
|
||||
type: 'String',
|
||||
default: undefined,
|
||||
} as StringVar,
|
||||
i15: {
|
||||
type: 'String',
|
||||
default: undefined,
|
||||
} as StringVar,
|
||||
formula: {
|
||||
type: 'String',
|
||||
default: '',
|
||||
@@ -50,41 +82,52 @@ const inputs = {
|
||||
waitForInput: {
|
||||
type: 'Boolean',
|
||||
default: true,
|
||||
} as BooleanVar
|
||||
} as BooleanVar,
|
||||
};
|
||||
|
||||
const outputs = {
|
||||
image: {
|
||||
type: 'Image'
|
||||
type: 'Image',
|
||||
} as ImageVar,
|
||||
result: {
|
||||
type: 'Number'
|
||||
type: 'Number',
|
||||
} as NumberVar,
|
||||
}
|
||||
resultString: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
};
|
||||
|
||||
export default class Eval extends Node<typeof inputs, typeof outputs> {
|
||||
constructor() {
|
||||
super('Eval', inputs, outputs);
|
||||
}
|
||||
|
||||
interpolate() {
|
||||
return this.in.formula.value.replace(/\{(\w+)\}/g, (_, paramName) =>
|
||||
get(this.in, [paramName, 'value'], 0)
|
||||
)
|
||||
interpolate(defaultValue: any) {
|
||||
return this.in.formula.value.replace(/\{((i\d)+)}/g, (originalString, paramName) => {
|
||||
return _.get(this.in, [paramName, 'value'], defaultValue);
|
||||
});
|
||||
}
|
||||
|
||||
hasAllConnectedInputsValue() {
|
||||
if (this.in.waitForInput.value) {
|
||||
return super.hasAllConnectedInputsValue();
|
||||
if (!this.in.waitForInput.value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const inputName of Object.keys(this.in.variables)) {
|
||||
// allow empty string
|
||||
if (this.in[inputName].output && !this.in[inputName].value && !_.isString(this.in[inputName].value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
evaluate() {
|
||||
try {
|
||||
this.out.result.value = eval(this.interpolate());
|
||||
this.out.result.value = eval(this.interpolate(0));
|
||||
this.out.resultString.value = eval(this.interpolate(''));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
console.error(e, this.in);
|
||||
}
|
||||
this.out.image.value = this.in.image.value;
|
||||
}
|
||||
|
||||
62
src/nodes/ImgToBase64.ts
Normal file
62
src/nodes/ImgToBase64.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import {
|
||||
ImageVar,
|
||||
StringVar,
|
||||
} from './io/AbstractIOSet';
|
||||
import Node from './Node';
|
||||
import {canvasPool2D} from '../canvas/CanvasPool';
|
||||
import {waitForMedia} from '../utils';
|
||||
import {mediaSize} from './canvas';
|
||||
|
||||
const inputs = {
|
||||
image: {
|
||||
type: 'Image',
|
||||
} as ImageVar,
|
||||
type: {
|
||||
type: 'String',
|
||||
enum: ['image/png', 'image/jpeg', 'image/webp', 'image/gif'],
|
||||
default: 'image/png',
|
||||
} as StringVar,
|
||||
};
|
||||
|
||||
const outputs = {
|
||||
base64: {
|
||||
type: 'String',
|
||||
} as StringVar,
|
||||
};
|
||||
|
||||
export default class ImgToBase64 extends Node<typeof inputs, typeof outputs> {
|
||||
constructor(options = {}) {
|
||||
super('ImgToBase64', inputs, outputs);
|
||||
}
|
||||
|
||||
async _update() {
|
||||
const media = this.in.image.value;
|
||||
|
||||
if (!media) {
|
||||
return;
|
||||
}
|
||||
await waitForMedia(media);
|
||||
const {width, height} = mediaSize(media);
|
||||
|
||||
const canvas = canvasPool2D.createCanvas();
|
||||
canvas.acquire();
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
// @ts-ignore
|
||||
canvas.getContext('2d').drawImage(media, 0, 0, width, height);
|
||||
|
||||
const blob = await canvas.convertToBlob({type: this.in.type.value});
|
||||
|
||||
this.out.base64.value = await new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
resolve(reader.result as string);
|
||||
};
|
||||
reader.onerror = (e) => {
|
||||
reject(e);
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user