@xyo-network/image-thumbnail-plugin 2.70.8 → 2.70.9
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.
- package/dist/cjs/Witness/Witness.js +159 -12
- package/dist/cjs/Witness/Witness.js.map +1 -1
- package/dist/docs.json +2978 -1705
- package/dist/esm/Witness/Witness.js +153 -12
- package/dist/esm/Witness/Witness.js.map +1 -1
- package/dist/types/Plugin.d.ts +5 -0
- package/dist/types/Plugin.d.ts.map +1 -1
- package/dist/types/Witness/Config.d.ts +5 -0
- package/dist/types/Witness/Config.d.ts.map +1 -1
- package/dist/types/Witness/Witness.d.ts +17 -2
- package/dist/types/Witness/Witness.d.ts.map +1 -1
- package/package.json +10 -9
- package/src/Witness/Config.ts +5 -0
- package/src/Witness/Witness.ts +170 -16
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { axios } from '@xyo-network/axios';
|
|
2
2
|
import { PayloadHasher } from '@xyo-network/core';
|
|
3
3
|
import { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin';
|
|
4
|
+
import { isPayload, ModuleErrorSchema } from '@xyo-network/payload-model';
|
|
4
5
|
import { AbstractWitness } from '@xyo-network/witness';
|
|
5
6
|
import { subClass } from 'gm';
|
|
6
7
|
import { sha256 } from 'hash-wasm';
|
|
7
8
|
import compact from 'lodash/compact';
|
|
9
|
+
import isBuffer from 'lodash/isBuffer';
|
|
10
|
+
import { LRUCache } from 'lru-cache';
|
|
8
11
|
import shajs from 'sha.js';
|
|
12
|
+
import Url from 'url-parse';
|
|
9
13
|
import { ImageThumbnailWitnessConfigSchema } from './Config';
|
|
14
|
+
//TODO: Break this into two Witnesses?
|
|
10
15
|
const gm = subClass({ imageMagick: '7+' });
|
|
11
16
|
export const binaryToSha256 = async (data) => {
|
|
12
17
|
await PayloadHasher.wasmInitialized;
|
|
@@ -21,23 +26,149 @@ export const binaryToSha256 = async (data) => {
|
|
|
21
26
|
// eslint-disable-next-line deprecation/deprecation
|
|
22
27
|
return shajs('sha256').update(data).digest().toString();
|
|
23
28
|
};
|
|
29
|
+
const checkIpfsUrl = (urlToCheck) => {
|
|
30
|
+
const url = new Url(urlToCheck);
|
|
31
|
+
let protocol = url.protocol;
|
|
32
|
+
let host = url.host;
|
|
33
|
+
let path = url.pathname;
|
|
34
|
+
const query = url.query;
|
|
35
|
+
if (protocol === 'ipfs:') {
|
|
36
|
+
protocol = 'https:';
|
|
37
|
+
host = 'cloudflare-ipfs.com';
|
|
38
|
+
path = url.host === 'ipfs' ? `ipfs${path}` : `ipfs/${url.host}${path}`;
|
|
39
|
+
const root = `${protocol}//${host}/${path}`;
|
|
40
|
+
return query?.length > 0 ? `root?${query}` : root;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return urlToCheck;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const checkDataUrl = (url) => {
|
|
47
|
+
if (url.startsWith('data:image')) {
|
|
48
|
+
const data = url.split(',')[1];
|
|
49
|
+
if (data) {
|
|
50
|
+
const buffer = Buffer.from(Uint8Array.from(atob(data), (c) => c.charCodeAt(0)));
|
|
51
|
+
console.log(`data buffer: ${buffer.length}`);
|
|
52
|
+
return [buffer, undefined];
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const error = {
|
|
56
|
+
message: 'Invalid data Url',
|
|
57
|
+
schema: ModuleErrorSchema,
|
|
58
|
+
url,
|
|
59
|
+
};
|
|
60
|
+
return [undefined, error];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
return [undefined, undefined];
|
|
65
|
+
}
|
|
66
|
+
};
|
|
24
67
|
export class ImageThumbnailWitness extends AbstractWitness {
|
|
25
68
|
static configSchemas = [ImageThumbnailWitnessConfigSchema];
|
|
69
|
+
_cache;
|
|
70
|
+
get cache() {
|
|
71
|
+
this._cache = this._cache ?? new LRUCache({ max: this.maxCacheEntries });
|
|
72
|
+
return this._cache;
|
|
73
|
+
}
|
|
74
|
+
get encoding() {
|
|
75
|
+
return this.config.encoding ?? 'PNG';
|
|
76
|
+
}
|
|
77
|
+
get height() {
|
|
78
|
+
return this.config.height ?? 128;
|
|
79
|
+
}
|
|
80
|
+
get maxCacheEntries() {
|
|
81
|
+
return this.config.maxCacheEntries ?? 5000;
|
|
82
|
+
}
|
|
83
|
+
get quality() {
|
|
84
|
+
return this.config.quality ?? 50;
|
|
85
|
+
}
|
|
86
|
+
get width() {
|
|
87
|
+
return this.config.width ?? 128;
|
|
88
|
+
}
|
|
26
89
|
async observeHandler(payloads = []) {
|
|
27
|
-
|
|
90
|
+
const responsePairs = compact(await Promise.all(payloads.map(async ({ url }) => {
|
|
91
|
+
const cachedResult = this.cache.get(url);
|
|
92
|
+
if (cachedResult) {
|
|
93
|
+
return [url, cachedResult];
|
|
94
|
+
}
|
|
95
|
+
//if it is a data URL, return a Buffer
|
|
96
|
+
const [dataBuffer, dataError] = checkDataUrl(url);
|
|
97
|
+
if (dataBuffer) {
|
|
98
|
+
return [url, dataBuffer];
|
|
99
|
+
}
|
|
100
|
+
if (dataError) {
|
|
101
|
+
return [url, dataError];
|
|
102
|
+
}
|
|
28
103
|
//if it is ipfs, go through cloud flair
|
|
29
|
-
const mutatedUrl = url
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
104
|
+
const mutatedUrl = checkIpfsUrl(url);
|
|
105
|
+
try {
|
|
106
|
+
return [
|
|
107
|
+
url,
|
|
108
|
+
await axios.get(mutatedUrl, {
|
|
109
|
+
responseType: 'arraybuffer',
|
|
110
|
+
}),
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
catch (ex) {
|
|
114
|
+
const axiosError = ex;
|
|
115
|
+
if (axiosError.isAxiosError) {
|
|
116
|
+
//selectively pick fields from AxiosError
|
|
117
|
+
const errorPayload = {
|
|
118
|
+
code: axiosError.code,
|
|
119
|
+
message: axiosError.message,
|
|
120
|
+
schema: ModuleErrorSchema,
|
|
121
|
+
status: axiosError.status,
|
|
122
|
+
url,
|
|
123
|
+
};
|
|
124
|
+
return [url, errorPayload];
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
throw ex;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
})));
|
|
131
|
+
return compact(await Promise.all(responsePairs.map(async ([url, urlResult]) => {
|
|
132
|
+
if (isPayload(urlResult)) {
|
|
133
|
+
this.cache.set(url, urlResult);
|
|
134
|
+
return urlResult;
|
|
135
|
+
}
|
|
136
|
+
let sourceBuffer;
|
|
137
|
+
if (isBuffer(urlResult)) {
|
|
138
|
+
sourceBuffer = urlResult;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
const response = urlResult;
|
|
142
|
+
if (response.status >= 200 && response.status < 300) {
|
|
143
|
+
const contentType = response.headers['content-type']?.toString();
|
|
144
|
+
if (contentType.split('/')[0] !== 'image') {
|
|
145
|
+
const error = {
|
|
146
|
+
message: `Invalid file type: ${contentType}`,
|
|
147
|
+
schema: ModuleErrorSchema,
|
|
148
|
+
url,
|
|
149
|
+
};
|
|
150
|
+
this.cache.set(url, error);
|
|
151
|
+
return error;
|
|
152
|
+
}
|
|
153
|
+
sourceBuffer = Buffer.from(response.data, 'binary');
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
const error = {
|
|
157
|
+
schema: ModuleErrorSchema,
|
|
158
|
+
status: response.status,
|
|
159
|
+
url,
|
|
160
|
+
};
|
|
161
|
+
this.cache.set(url, error);
|
|
162
|
+
return error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
35
166
|
const thumb = await new Promise((resolve, reject) => {
|
|
36
|
-
gm(
|
|
37
|
-
.quality(
|
|
38
|
-
.resize(
|
|
167
|
+
gm(sourceBuffer)
|
|
168
|
+
.quality(this.quality)
|
|
169
|
+
.resize(this.width, this.height)
|
|
39
170
|
.flatten()
|
|
40
|
-
.toBuffer(
|
|
171
|
+
.toBuffer(this.encoding, (error, buffer) => {
|
|
41
172
|
if (error) {
|
|
42
173
|
reject(error);
|
|
43
174
|
}
|
|
@@ -48,12 +179,22 @@ export class ImageThumbnailWitness extends AbstractWitness {
|
|
|
48
179
|
});
|
|
49
180
|
const result = {
|
|
50
181
|
schema: ImageThumbnailSchema,
|
|
51
|
-
sourceHash: await binaryToSha256(
|
|
182
|
+
sourceHash: await binaryToSha256(sourceBuffer),
|
|
52
183
|
sourceUrl: url,
|
|
53
184
|
url: `data:image/png;base64,${thumb.toString('base64')}`,
|
|
54
185
|
};
|
|
186
|
+
this.cache.set(url, result);
|
|
55
187
|
return result;
|
|
56
188
|
}
|
|
189
|
+
catch (ex) {
|
|
190
|
+
const error = {
|
|
191
|
+
message: 'Failed to resize image',
|
|
192
|
+
schema: ModuleErrorSchema,
|
|
193
|
+
url,
|
|
194
|
+
};
|
|
195
|
+
this.cache.set(url, error);
|
|
196
|
+
return error;
|
|
197
|
+
}
|
|
57
198
|
})));
|
|
58
199
|
}
|
|
59
200
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Witness.js","sourceRoot":"","sources":["../../../src/Witness/Witness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"Witness.js","sourceRoot":"","sources":["../../../src/Witness/Witness.ts"],"names":[],"mappings":"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"}
|
package/dist/types/Plugin.d.ts
CHANGED
|
@@ -36,7 +36,12 @@ export declare const ImageThumbnailPlugin: () => import("@xyo-network/payloadset
|
|
|
36
36
|
schema: "network.xyo.payload.set";
|
|
37
37
|
}) | undefined;
|
|
38
38
|
} & {
|
|
39
|
+
encoding?: "PNG" | undefined;
|
|
40
|
+
height?: number | undefined;
|
|
41
|
+
maxCacheEntries?: number | undefined;
|
|
42
|
+
quality?: number | undefined;
|
|
39
43
|
schema: "network.xyo.image.thumbnail.witness.config";
|
|
44
|
+
width?: number | undefined;
|
|
40
45
|
}, "schema"> & {
|
|
41
46
|
schema: "network.xyo.image.thumbnail.witness.config";
|
|
42
47
|
}, "schema"> & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAA;AAEjD,eAAO,MAAM,oBAAoB
|
|
1
|
+
{"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAA;AAEjD,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAS9B,CAAA"}
|
|
@@ -2,6 +2,11 @@ import { WitnessConfig } from '@xyo-network/witness';
|
|
|
2
2
|
export declare const ImageThumbnailWitnessConfigSchema: "network.xyo.image.thumbnail.witness.config";
|
|
3
3
|
export type ImageThumbnailWitnessConfigSchema = typeof ImageThumbnailWitnessConfigSchema;
|
|
4
4
|
export type ImageThumbnailWitnessConfig = WitnessConfig<{
|
|
5
|
+
encoding?: 'PNG';
|
|
6
|
+
height?: number;
|
|
7
|
+
maxCacheEntries?: number;
|
|
8
|
+
quality?: number;
|
|
5
9
|
schema: ImageThumbnailWitnessConfigSchema;
|
|
10
|
+
width?: number;
|
|
6
11
|
}>;
|
|
7
12
|
//# sourceMappingURL=Config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Config.d.ts","sourceRoot":"","sources":["../../../src/Witness/Config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAEpD,eAAO,MAAM,iCAAiC,8CAAoD,CAAA;AAClG,MAAM,MAAM,iCAAiC,GAAG,OAAO,iCAAiC,CAAA;AAExF,MAAM,MAAM,2BAA2B,GAAG,aAAa,CAAC;IACtD,MAAM,EAAE,iCAAiC,CAAA;
|
|
1
|
+
{"version":3,"file":"Config.d.ts","sourceRoot":"","sources":["../../../src/Witness/Config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAEpD,eAAO,MAAM,iCAAiC,8CAAoD,CAAA;AAClG,MAAM,MAAM,iCAAiC,GAAG,OAAO,iCAAiC,CAAA;AAExF,MAAM,MAAM,2BAA2B,GAAG,aAAa,CAAC;IACtD,QAAQ,CAAC,EAAE,KAAK,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,iCAAiC,CAAA;IACzC,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAC,CAAA"}
|
|
@@ -1,10 +1,25 @@
|
|
|
1
|
-
import { ImageThumbnailPayload } from '@xyo-network/image-thumbnail-payload-plugin';
|
|
1
|
+
import { ImageThumbnailErrorPayload, ImageThumbnailPayload } from '@xyo-network/image-thumbnail-payload-plugin';
|
|
2
2
|
import { UrlPayload } from '@xyo-network/url-payload-plugin';
|
|
3
3
|
import { AbstractWitness } from '@xyo-network/witness';
|
|
4
|
+
import { LRUCache } from 'lru-cache';
|
|
4
5
|
import { ImageThumbnailWitnessParams } from './Params';
|
|
5
6
|
export declare const binaryToSha256: (data: Uint8Array) => Promise<string>;
|
|
6
7
|
export declare class ImageThumbnailWitness<TParams extends ImageThumbnailWitnessParams = ImageThumbnailWitnessParams> extends AbstractWitness<TParams> {
|
|
7
8
|
static configSchemas: "network.xyo.image.thumbnail.witness.config"[];
|
|
8
|
-
|
|
9
|
+
private _cache?;
|
|
10
|
+
get cache(): LRUCache<string, ImageThumbnailErrorPayload | (import("@xyo-network/payload-model").SchemaFields & import("@xyo-network/payload-model").PayloadFields & {
|
|
11
|
+
schema: "network.xyo.image.thumbnail";
|
|
12
|
+
sourceHash: string;
|
|
13
|
+
sourceUrl: string;
|
|
14
|
+
url: string;
|
|
15
|
+
} & {
|
|
16
|
+
schema: "network.xyo.image.thumbnail";
|
|
17
|
+
}), unknown>;
|
|
18
|
+
get encoding(): "PNG";
|
|
19
|
+
get height(): number;
|
|
20
|
+
get maxCacheEntries(): number;
|
|
21
|
+
get quality(): number;
|
|
22
|
+
get width(): number;
|
|
23
|
+
protected observeHandler(payloads?: UrlPayload[]): Promise<(ImageThumbnailPayload | ImageThumbnailErrorPayload)[]>;
|
|
9
24
|
}
|
|
10
25
|
//# sourceMappingURL=Witness.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Witness.d.ts","sourceRoot":"","sources":["../../../src/Witness/Witness.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAwB,MAAM,6CAA6C,CAAA;
|
|
1
|
+
{"version":3,"file":"Witness.d.ts","sourceRoot":"","sources":["../../../src/Witness/Witness.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,0BAA0B,EAAE,qBAAqB,EAAwB,MAAM,6CAA6C,CAAA;AAErI,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAKtD,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAKpC,OAAO,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAA;AAMtD,eAAO,MAAM,cAAc,SAAgB,UAAU,oBAWpD,CAAA;AAuCD,qBAAa,qBAAqB,CAAC,OAAO,SAAS,2BAA2B,GAAG,2BAA2B,CAAE,SAAQ,eAAe,CAAC,OAAO,CAAC;IAC5I,OAAgB,aAAa,iDAAsC;IAEnE,OAAO,CAAC,MAAM,CAAC,CAAsE;IAErF,IAAI,KAAK;;;;;;;iBAGR;IAED,IAAI,QAAQ,UAEX;IAED,IAAI,MAAM,WAET;IAED,IAAI,eAAe,WAElB;IAED,IAAI,OAAO,WAEV;IAED,IAAI,KAAK,WAER;cAEwB,cAAc,CAAC,QAAQ,GAAE,UAAU,EAAO,GAAG,OAAO,CAAC,CAAC,qBAAqB,GAAG,0BAA0B,CAAC,EAAE,CAAC;CAwHtI"}
|
package/package.json
CHANGED
|
@@ -10,17 +10,18 @@
|
|
|
10
10
|
"url": "https://github.com/XYOracleNetwork/sdk-xyo-client-js/issues"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@xyo-network/axios": "~2.70.
|
|
14
|
-
"@xyo-network/core": "~2.70.
|
|
15
|
-
"@xyo-network/image-thumbnail-payload-plugin": "~2.70.
|
|
16
|
-
"@xyo-network/module": "~2.70.
|
|
17
|
-
"@xyo-network/payload-model": "~2.70.
|
|
18
|
-
"@xyo-network/payloadset-plugin": "~2.70.
|
|
19
|
-
"@xyo-network/url-payload-plugin": "~2.70.
|
|
20
|
-
"@xyo-network/witness": "~2.70.
|
|
13
|
+
"@xyo-network/axios": "~2.70.11",
|
|
14
|
+
"@xyo-network/core": "~2.70.11",
|
|
15
|
+
"@xyo-network/image-thumbnail-payload-plugin": "~2.70.9",
|
|
16
|
+
"@xyo-network/module": "~2.70.11",
|
|
17
|
+
"@xyo-network/payload-model": "~2.70.11",
|
|
18
|
+
"@xyo-network/payloadset-plugin": "~2.70.11",
|
|
19
|
+
"@xyo-network/url-payload-plugin": "~2.70.11",
|
|
20
|
+
"@xyo-network/witness": "~2.70.11",
|
|
21
21
|
"gm": "^1.25.0",
|
|
22
22
|
"hash-wasm": "^4.9.0",
|
|
23
23
|
"lodash": "^4.17.21",
|
|
24
|
+
"lru-cache": "^10.0.0",
|
|
24
25
|
"sha.js": "^2.4.11"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
@@ -63,5 +64,5 @@
|
|
|
63
64
|
},
|
|
64
65
|
"sideEffects": false,
|
|
65
66
|
"types": "dist/types/index.d.ts",
|
|
66
|
-
"version": "2.70.
|
|
67
|
+
"version": "2.70.9"
|
|
67
68
|
}
|
package/src/Witness/Config.ts
CHANGED
|
@@ -5,5 +5,10 @@ export const ImageThumbnailWitnessConfigSchema = `${ImageThumbnailSchema}.witnes
|
|
|
5
5
|
export type ImageThumbnailWitnessConfigSchema = typeof ImageThumbnailWitnessConfigSchema
|
|
6
6
|
|
|
7
7
|
export type ImageThumbnailWitnessConfig = WitnessConfig<{
|
|
8
|
+
encoding?: 'PNG'
|
|
9
|
+
height?: number
|
|
10
|
+
maxCacheEntries?: number
|
|
11
|
+
quality?: number
|
|
8
12
|
schema: ImageThumbnailWitnessConfigSchema
|
|
13
|
+
width?: number
|
|
9
14
|
}>
|
package/src/Witness/Witness.ts
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
|
-
import { axios } from '@xyo-network/axios'
|
|
1
|
+
import { axios, AxiosError, AxiosResponse } from '@xyo-network/axios'
|
|
2
2
|
import { PayloadHasher } from '@xyo-network/core'
|
|
3
|
-
import { ImageThumbnailPayload, ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'
|
|
3
|
+
import { ImageThumbnailErrorPayload, ImageThumbnailPayload, ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'
|
|
4
|
+
import { isPayload, ModuleError, ModuleErrorSchema } from '@xyo-network/payload-model'
|
|
4
5
|
import { UrlPayload } from '@xyo-network/url-payload-plugin'
|
|
5
6
|
import { AbstractWitness } from '@xyo-network/witness'
|
|
6
7
|
import { subClass } from 'gm'
|
|
7
8
|
import { sha256 } from 'hash-wasm'
|
|
8
9
|
import compact from 'lodash/compact'
|
|
10
|
+
import isBuffer from 'lodash/isBuffer'
|
|
11
|
+
import { LRUCache } from 'lru-cache'
|
|
9
12
|
import shajs from 'sha.js'
|
|
13
|
+
import Url from 'url-parse'
|
|
10
14
|
|
|
11
15
|
import { ImageThumbnailWitnessConfigSchema } from './Config'
|
|
12
16
|
import { ImageThumbnailWitnessParams } from './Params'
|
|
13
17
|
|
|
18
|
+
//TODO: Break this into two Witnesses?
|
|
19
|
+
|
|
14
20
|
const gm = subClass({ imageMagick: '7+' })
|
|
15
21
|
|
|
16
22
|
export const binaryToSha256 = async (data: Uint8Array) => {
|
|
@@ -26,26 +32,165 @@ export const binaryToSha256 = async (data: Uint8Array) => {
|
|
|
26
32
|
return shajs('sha256').update(data).digest().toString()
|
|
27
33
|
}
|
|
28
34
|
|
|
35
|
+
const checkIpfsUrl = (urlToCheck: string) => {
|
|
36
|
+
const url = new Url(urlToCheck)
|
|
37
|
+
let protocol = url.protocol
|
|
38
|
+
let host = url.host
|
|
39
|
+
let path = url.pathname
|
|
40
|
+
const query = url.query
|
|
41
|
+
if (protocol === 'ipfs:') {
|
|
42
|
+
protocol = 'https:'
|
|
43
|
+
host = 'cloudflare-ipfs.com'
|
|
44
|
+
path = url.host === 'ipfs' ? `ipfs${path}` : `ipfs/${url.host}${path}`
|
|
45
|
+
const root = `${protocol}//${host}/${path}`
|
|
46
|
+
return query?.length > 0 ? `root?${query}` : root
|
|
47
|
+
} else {
|
|
48
|
+
return urlToCheck
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const checkDataUrl = (url: string): [Buffer | undefined, ImageThumbnailErrorPayload | undefined] => {
|
|
53
|
+
if (url.startsWith('data:image')) {
|
|
54
|
+
const data = url.split(',')[1]
|
|
55
|
+
if (data) {
|
|
56
|
+
const buffer = Buffer.from(Uint8Array.from(atob(data), (c) => c.charCodeAt(0)))
|
|
57
|
+
console.log(`data buffer: ${buffer.length}`)
|
|
58
|
+
return [buffer, undefined]
|
|
59
|
+
} else {
|
|
60
|
+
const error: ImageThumbnailErrorPayload = {
|
|
61
|
+
message: 'Invalid data Url',
|
|
62
|
+
schema: ModuleErrorSchema,
|
|
63
|
+
url,
|
|
64
|
+
}
|
|
65
|
+
return [undefined, error]
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
return [undefined, undefined]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
29
72
|
export class ImageThumbnailWitness<TParams extends ImageThumbnailWitnessParams = ImageThumbnailWitnessParams> extends AbstractWitness<TParams> {
|
|
30
73
|
static override configSchemas = [ImageThumbnailWitnessConfigSchema]
|
|
31
74
|
|
|
32
|
-
|
|
33
|
-
|
|
75
|
+
private _cache?: LRUCache<string, ImageThumbnailPayload | ImageThumbnailErrorPayload>
|
|
76
|
+
|
|
77
|
+
get cache() {
|
|
78
|
+
this._cache = this._cache ?? new LRUCache<string, ImageThumbnailPayload | ImageThumbnailErrorPayload>({ max: this.maxCacheEntries })
|
|
79
|
+
return this._cache
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get encoding() {
|
|
83
|
+
return this.config.encoding ?? 'PNG'
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get height() {
|
|
87
|
+
return this.config.height ?? 128
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get maxCacheEntries() {
|
|
91
|
+
return this.config.maxCacheEntries ?? 5000
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
get quality() {
|
|
95
|
+
return this.config.quality ?? 50
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get width() {
|
|
99
|
+
return this.config.width ?? 128
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
protected override async observeHandler(payloads: UrlPayload[] = []): Promise<(ImageThumbnailPayload | ImageThumbnailErrorPayload)[]> {
|
|
103
|
+
const responsePairs = compact(
|
|
34
104
|
await Promise.all(
|
|
35
|
-
payloads.map(async ({ url }) => {
|
|
105
|
+
payloads.map<Promise<[string, ImageThumbnailPayload | ImageThumbnailErrorPayload | AxiosResponse | Buffer]>>(async ({ url }) => {
|
|
106
|
+
const cachedResult = this.cache.get(url)
|
|
107
|
+
if (cachedResult) {
|
|
108
|
+
return [url, cachedResult]
|
|
109
|
+
}
|
|
110
|
+
//if it is a data URL, return a Buffer
|
|
111
|
+
const [dataBuffer, dataError] = checkDataUrl(url)
|
|
112
|
+
|
|
113
|
+
if (dataBuffer) {
|
|
114
|
+
return [url, dataBuffer]
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (dataError) {
|
|
118
|
+
return [url, dataError]
|
|
119
|
+
}
|
|
120
|
+
|
|
36
121
|
//if it is ipfs, go through cloud flair
|
|
37
|
-
const mutatedUrl = url
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
122
|
+
const mutatedUrl = checkIpfsUrl(url)
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
return [
|
|
126
|
+
url,
|
|
127
|
+
await axios.get(mutatedUrl, {
|
|
128
|
+
responseType: 'arraybuffer',
|
|
129
|
+
}),
|
|
130
|
+
]
|
|
131
|
+
} catch (ex) {
|
|
132
|
+
const axiosError = ex as AxiosError
|
|
133
|
+
if (axiosError.isAxiosError) {
|
|
134
|
+
//selectively pick fields from AxiosError
|
|
135
|
+
const errorPayload: ImageThumbnailErrorPayload = {
|
|
136
|
+
code: axiosError.code,
|
|
137
|
+
message: axiosError.message,
|
|
138
|
+
schema: ModuleErrorSchema,
|
|
139
|
+
status: axiosError.status,
|
|
140
|
+
url,
|
|
141
|
+
}
|
|
142
|
+
return [url, errorPayload]
|
|
143
|
+
} else {
|
|
144
|
+
throw ex
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}),
|
|
148
|
+
),
|
|
149
|
+
)
|
|
150
|
+
return compact(
|
|
151
|
+
await Promise.all(
|
|
152
|
+
responsePairs.map(async ([url, urlResult]) => {
|
|
153
|
+
if (isPayload(urlResult)) {
|
|
154
|
+
this.cache.set(url, urlResult)
|
|
155
|
+
return urlResult
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let sourceBuffer: Buffer
|
|
159
|
+
|
|
160
|
+
if (isBuffer(urlResult)) {
|
|
161
|
+
sourceBuffer = urlResult as Buffer
|
|
162
|
+
} else {
|
|
163
|
+
const response = urlResult as AxiosResponse
|
|
164
|
+
|
|
165
|
+
if (response.status >= 200 && response.status < 300) {
|
|
166
|
+
const contentType = response.headers['content-type']?.toString()
|
|
167
|
+
if (contentType.split('/')[0] !== 'image') {
|
|
168
|
+
const error: ImageThumbnailErrorPayload = {
|
|
169
|
+
message: `Invalid file type: ${contentType}`,
|
|
170
|
+
schema: ModuleErrorSchema,
|
|
171
|
+
url,
|
|
172
|
+
}
|
|
173
|
+
this.cache.set(url, error)
|
|
174
|
+
return error
|
|
175
|
+
}
|
|
176
|
+
sourceBuffer = Buffer.from(response.data, 'binary')
|
|
177
|
+
} else {
|
|
178
|
+
const error: ImageThumbnailErrorPayload = {
|
|
179
|
+
schema: ModuleErrorSchema,
|
|
180
|
+
status: response.status,
|
|
181
|
+
url,
|
|
182
|
+
}
|
|
183
|
+
this.cache.set(url, error)
|
|
184
|
+
return error
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
43
188
|
const thumb = await new Promise<Buffer>((resolve, reject) => {
|
|
44
|
-
gm(
|
|
45
|
-
.quality(
|
|
46
|
-
.resize(
|
|
189
|
+
gm(sourceBuffer)
|
|
190
|
+
.quality(this.quality)
|
|
191
|
+
.resize(this.width, this.height)
|
|
47
192
|
.flatten()
|
|
48
|
-
.toBuffer(
|
|
193
|
+
.toBuffer(this.encoding, (error, buffer) => {
|
|
49
194
|
if (error) {
|
|
50
195
|
reject(error)
|
|
51
196
|
} else {
|
|
@@ -55,11 +200,20 @@ export class ImageThumbnailWitness<TParams extends ImageThumbnailWitnessParams =
|
|
|
55
200
|
})
|
|
56
201
|
const result: ImageThumbnailPayload = {
|
|
57
202
|
schema: ImageThumbnailSchema,
|
|
58
|
-
sourceHash: await binaryToSha256(
|
|
203
|
+
sourceHash: await binaryToSha256(sourceBuffer),
|
|
59
204
|
sourceUrl: url,
|
|
60
205
|
url: `data:image/png;base64,${thumb.toString('base64')}`,
|
|
61
206
|
}
|
|
207
|
+
this.cache.set(url, result)
|
|
62
208
|
return result
|
|
209
|
+
} catch (ex) {
|
|
210
|
+
const error: ImageThumbnailErrorPayload = {
|
|
211
|
+
message: 'Failed to resize image',
|
|
212
|
+
schema: ModuleErrorSchema,
|
|
213
|
+
url,
|
|
214
|
+
}
|
|
215
|
+
this.cache.set(url, error)
|
|
216
|
+
return error
|
|
63
217
|
}
|
|
64
218
|
}),
|
|
65
219
|
),
|