@xyo-network/image-thumbnail-plugin 2.77.2 → 2.77.4
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/node/Witness/Witness.d.cts.map +1 -1
- package/dist/node/Witness/Witness.d.mts.map +1 -1
- package/dist/node/Witness/Witness.d.ts.map +1 -1
- package/dist/node/index.js +16 -9
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +16 -9
- package/dist/node/index.mjs.map +1 -1
- package/package.json +17 -17
- package/src/Witness/Witness.ts +9 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Witness.d.ts","sourceRoot":"","sources":["../../../src/Witness/Witness.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAG/D,OAAO,EAAE,cAAc,EAAwB,MAAM,6CAA6C,CAAA;AAClG,OAAO,EAAE,UAAU,EAAa,MAAM,iCAAiC,CAAA;AASvE,OAAO,EAAE,sBAAsB,EAAqC,MAAM,UAAU,CAAA;AAEpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAA;AAStD,MAAM,WAAW,0BAA2B,SAAQ,KAAK;IACvD,IAAI,EAAE,4BAA4B,CAAA;IAClC,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,QAAS,SAAQ,KAAK;IACrC,IAAI,EAAE,MAAM,CAAA;CACb;AAED,qBAAa,qBAAqB,CAAC,OAAO,SAAS,2BAA2B,GAAG,2BAA2B,CAAE,SAAQ,eAAe,CAAC,OAAO,CAAC;IAC5I,OAAgB,aAAa,iDAAsC;IAEnE,OAAO,CAAC,UAAU,CAAwC;IAE1D,IAAI,QAAQ,2BAEX;IAED,IAAI,MAAM,WAET;IAED,IAAI,UAAU,WAEb;IAED,IAAI,iBAAiB,WAEpB;IAED,IAAI,OAAO,WAEV;IAED,IAAI,KAAK,WAER;mBAEoB,cAAc;IAanC,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAgBhC;;;;;OAKG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM;cAiBN,cAAc,CAAC,QAAQ,GAAE,UAAU,EAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;YAkCjF,sBAAsB;IAiBpC;;;;OAIG;YACW,wBAAwB;YAMxB,QAAQ;
|
|
1
|
+
{"version":3,"file":"Witness.d.ts","sourceRoot":"","sources":["../../../src/Witness/Witness.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAG/D,OAAO,EAAE,cAAc,EAAwB,MAAM,6CAA6C,CAAA;AAClG,OAAO,EAAE,UAAU,EAAa,MAAM,iCAAiC,CAAA;AASvE,OAAO,EAAE,sBAAsB,EAAqC,MAAM,UAAU,CAAA;AAEpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAA;AAStD,MAAM,WAAW,0BAA2B,SAAQ,KAAK;IACvD,IAAI,EAAE,4BAA4B,CAAA;IAClC,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,QAAS,SAAQ,KAAK;IACrC,IAAI,EAAE,MAAM,CAAA;CACb;AAED,qBAAa,qBAAqB,CAAC,OAAO,SAAS,2BAA2B,GAAG,2BAA2B,CAAE,SAAQ,eAAe,CAAC,OAAO,CAAC;IAC5I,OAAgB,aAAa,iDAAsC;IAEnE,OAAO,CAAC,UAAU,CAAwC;IAE1D,IAAI,QAAQ,2BAEX;IAED,IAAI,MAAM,WAET;IAED,IAAI,UAAU,WAEb;IAED,IAAI,iBAAiB,WAEpB;IAED,IAAI,OAAO,WAEV;IAED,IAAI,KAAK,WAER;mBAEoB,cAAc;IAanC,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAgBhC;;;;;OAKG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM;cAiBN,cAAc,CAAC,QAAQ,GAAE,UAAU,EAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;YAkCjF,sBAAsB;IAiBpC;;;;OAIG;YACW,wBAAwB;YAMxB,QAAQ;CAqIvB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Witness.d.ts","sourceRoot":"","sources":["../../../src/Witness/Witness.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAG/D,OAAO,EAAE,cAAc,EAAwB,MAAM,6CAA6C,CAAA;AAClG,OAAO,EAAE,UAAU,EAAa,MAAM,iCAAiC,CAAA;AASvE,OAAO,EAAE,sBAAsB,EAAqC,MAAM,UAAU,CAAA;AAEpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAA;AAStD,MAAM,WAAW,0BAA2B,SAAQ,KAAK;IACvD,IAAI,EAAE,4BAA4B,CAAA;IAClC,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,QAAS,SAAQ,KAAK;IACrC,IAAI,EAAE,MAAM,CAAA;CACb;AAED,qBAAa,qBAAqB,CAAC,OAAO,SAAS,2BAA2B,GAAG,2BAA2B,CAAE,SAAQ,eAAe,CAAC,OAAO,CAAC;IAC5I,OAAgB,aAAa,iDAAsC;IAEnE,OAAO,CAAC,UAAU,CAAwC;IAE1D,IAAI,QAAQ,2BAEX;IAED,IAAI,MAAM,WAET;IAED,IAAI,UAAU,WAEb;IAED,IAAI,iBAAiB,WAEpB;IAED,IAAI,OAAO,WAEV;IAED,IAAI,KAAK,WAER;mBAEoB,cAAc;IAanC,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAgBhC;;;;;OAKG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM;cAiBN,cAAc,CAAC,QAAQ,GAAE,UAAU,EAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;YAkCjF,sBAAsB;IAiBpC;;;;OAIG;YACW,wBAAwB;YAMxB,QAAQ;
|
|
1
|
+
{"version":3,"file":"Witness.d.ts","sourceRoot":"","sources":["../../../src/Witness/Witness.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAG/D,OAAO,EAAE,cAAc,EAAwB,MAAM,6CAA6C,CAAA;AAClG,OAAO,EAAE,UAAU,EAAa,MAAM,iCAAiC,CAAA;AASvE,OAAO,EAAE,sBAAsB,EAAqC,MAAM,UAAU,CAAA;AAEpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAA;AAStD,MAAM,WAAW,0BAA2B,SAAQ,KAAK;IACvD,IAAI,EAAE,4BAA4B,CAAA;IAClC,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,QAAS,SAAQ,KAAK;IACrC,IAAI,EAAE,MAAM,CAAA;CACb;AAED,qBAAa,qBAAqB,CAAC,OAAO,SAAS,2BAA2B,GAAG,2BAA2B,CAAE,SAAQ,eAAe,CAAC,OAAO,CAAC;IAC5I,OAAgB,aAAa,iDAAsC;IAEnE,OAAO,CAAC,UAAU,CAAwC;IAE1D,IAAI,QAAQ,2BAEX;IAED,IAAI,MAAM,WAET;IAED,IAAI,UAAU,WAEb;IAED,IAAI,iBAAiB,WAEpB;IAED,IAAI,OAAO,WAEV;IAED,IAAI,KAAK,WAER;mBAEoB,cAAc;IAanC,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAgBhC;;;;;OAKG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM;cAiBN,cAAc,CAAC,QAAQ,GAAE,UAAU,EAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;YAkCjF,sBAAsB;IAiBpC;;;;OAIG;YACW,wBAAwB;YAMxB,QAAQ;CAqIvB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Witness.d.ts","sourceRoot":"","sources":["../../../src/Witness/Witness.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAG/D,OAAO,EAAE,cAAc,EAAwB,MAAM,6CAA6C,CAAA;AAClG,OAAO,EAAE,UAAU,EAAa,MAAM,iCAAiC,CAAA;AASvE,OAAO,EAAE,sBAAsB,EAAqC,MAAM,UAAU,CAAA;AAEpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAA;AAStD,MAAM,WAAW,0BAA2B,SAAQ,KAAK;IACvD,IAAI,EAAE,4BAA4B,CAAA;IAClC,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,QAAS,SAAQ,KAAK;IACrC,IAAI,EAAE,MAAM,CAAA;CACb;AAED,qBAAa,qBAAqB,CAAC,OAAO,SAAS,2BAA2B,GAAG,2BAA2B,CAAE,SAAQ,eAAe,CAAC,OAAO,CAAC;IAC5I,OAAgB,aAAa,iDAAsC;IAEnE,OAAO,CAAC,UAAU,CAAwC;IAE1D,IAAI,QAAQ,2BAEX;IAED,IAAI,MAAM,WAET;IAED,IAAI,UAAU,WAEb;IAED,IAAI,iBAAiB,WAEpB;IAED,IAAI,OAAO,WAEV;IAED,IAAI,KAAK,WAER;mBAEoB,cAAc;IAanC,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAgBhC;;;;;OAKG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM;cAiBN,cAAc,CAAC,QAAQ,GAAE,UAAU,EAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;YAkCjF,sBAAsB;IAiBpC;;;;OAIG;YACW,wBAAwB;YAMxB,QAAQ;
|
|
1
|
+
{"version":3,"file":"Witness.d.ts","sourceRoot":"","sources":["../../../src/Witness/Witness.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAG/D,OAAO,EAAE,cAAc,EAAwB,MAAM,6CAA6C,CAAA;AAClG,OAAO,EAAE,UAAU,EAAa,MAAM,iCAAiC,CAAA;AASvE,OAAO,EAAE,sBAAsB,EAAqC,MAAM,UAAU,CAAA;AAEpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAA;AAStD,MAAM,WAAW,0BAA2B,SAAQ,KAAK;IACvD,IAAI,EAAE,4BAA4B,CAAA;IAClC,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,QAAS,SAAQ,KAAK;IACrC,IAAI,EAAE,MAAM,CAAA;CACb;AAED,qBAAa,qBAAqB,CAAC,OAAO,SAAS,2BAA2B,GAAG,2BAA2B,CAAE,SAAQ,eAAe,CAAC,OAAO,CAAC;IAC5I,OAAgB,aAAa,iDAAsC;IAEnE,OAAO,CAAC,UAAU,CAAwC;IAE1D,IAAI,QAAQ,2BAEX;IAED,IAAI,MAAM,WAET;IAED,IAAI,UAAU,WAEb;IAED,IAAI,iBAAiB,WAEpB;IAED,IAAI,OAAO,WAEV;IAED,IAAI,KAAK,WAER;mBAEoB,cAAc;IAanC,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAgBhC;;;;;OAKG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM;cAiBN,cAAc,CAAC,QAAQ,GAAE,UAAU,EAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;YAkCjF,sBAAsB;IAiBpC;;;;OAIG;YACW,wBAAwB;YAMxB,QAAQ;CAqIvB"}
|
package/dist/node/index.js
CHANGED
|
@@ -226,7 +226,7 @@ var ImageThumbnailWitness = class _ImageThumbnailWitness extends import_abstract
|
|
|
226
226
|
}
|
|
227
227
|
// eslint-disable-next-line complexity
|
|
228
228
|
async fromHttp(url, sourceUrl) {
|
|
229
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
229
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
230
230
|
let response;
|
|
231
231
|
let dnsResult;
|
|
232
232
|
try {
|
|
@@ -236,7 +236,7 @@ var ImageThumbnailWitness = class _ImageThumbnailWitness extends import_abstract
|
|
|
236
236
|
const error = ex;
|
|
237
237
|
const result2 = {
|
|
238
238
|
http: {
|
|
239
|
-
|
|
239
|
+
code: error.code
|
|
240
240
|
},
|
|
241
241
|
schema: import_image_thumbnail_payload_plugin2.ImageThumbnailSchema,
|
|
242
242
|
sourceUrl: sourceUrl ?? url
|
|
@@ -252,12 +252,19 @@ var ImageThumbnailWitness = class _ImageThumbnailWitness extends import_abstract
|
|
|
252
252
|
if (axiosError.isAxiosError) {
|
|
253
253
|
const result2 = {
|
|
254
254
|
http: {
|
|
255
|
-
ipAddress: dnsResult[0]
|
|
256
|
-
status: (_a = axiosError == null ? void 0 : axiosError.response) == null ? void 0 : _a.status
|
|
255
|
+
ipAddress: dnsResult[0]
|
|
257
256
|
},
|
|
258
257
|
schema: import_image_thumbnail_payload_plugin2.ImageThumbnailSchema,
|
|
259
258
|
sourceUrl: sourceUrl ?? url
|
|
260
259
|
};
|
|
260
|
+
if (((_a = axiosError == null ? void 0 : axiosError.response) == null ? void 0 : _a.status) !== void 0) {
|
|
261
|
+
result2.http = result2.http ?? {};
|
|
262
|
+
result2.http.status = (_b = axiosError == null ? void 0 : axiosError.response) == null ? void 0 : _b.status;
|
|
263
|
+
}
|
|
264
|
+
if ((axiosError == null ? void 0 : axiosError.code) !== void 0) {
|
|
265
|
+
result2.http = result2.http ?? {};
|
|
266
|
+
result2.http.code = axiosError == null ? void 0 : axiosError.code;
|
|
267
|
+
}
|
|
261
268
|
return result2;
|
|
262
269
|
} else {
|
|
263
270
|
throw ex;
|
|
@@ -271,7 +278,7 @@ var ImageThumbnailWitness = class _ImageThumbnailWitness extends import_abstract
|
|
|
271
278
|
sourceUrl: sourceUrl ?? url
|
|
272
279
|
};
|
|
273
280
|
if (response.status >= 200 && response.status < 300) {
|
|
274
|
-
const contentType = (
|
|
281
|
+
const contentType = (_c = response.headers["content-type"]) == null ? void 0 : _c.toString();
|
|
275
282
|
const [mediaType, fileType] = (contentType == null ? void 0 : contentType.split("/")) ?? ["", ""];
|
|
276
283
|
result.mime = result.mime ?? {};
|
|
277
284
|
result.mime.returned = mediaType;
|
|
@@ -280,7 +287,7 @@ var ImageThumbnailWitness = class _ImageThumbnailWitness extends import_abstract
|
|
|
280
287
|
result.mime.detected = await import_file_type.default.fromBuffer(sourceBuffer);
|
|
281
288
|
} catch (ex) {
|
|
282
289
|
const error = ex;
|
|
283
|
-
(
|
|
290
|
+
(_d = this.logger) == null ? void 0 : _d.error(`FileType error: ${error.message}`);
|
|
284
291
|
}
|
|
285
292
|
const processImage = async (encoding2) => {
|
|
286
293
|
result.sourceHash = await _ImageThumbnailWitness.binaryToSha256(sourceBuffer);
|
|
@@ -317,16 +324,16 @@ var ImageThumbnailWitness = class _ImageThumbnailWitness extends import_abstract
|
|
|
317
324
|
break;
|
|
318
325
|
}
|
|
319
326
|
default: {
|
|
320
|
-
const [detectedMediaType] = ((
|
|
327
|
+
const [detectedMediaType] = ((_f = (_e = result.mime.detected) == null ? void 0 : _e.mime) == null ? void 0 : _f.split("/")) ?? ["", ""];
|
|
321
328
|
switch (detectedMediaType) {
|
|
322
329
|
case "image": {
|
|
323
330
|
await processImage();
|
|
324
|
-
result.mime.type = (
|
|
331
|
+
result.mime.type = (_g = result.mime.detected) == null ? void 0 : _g.mime;
|
|
325
332
|
break;
|
|
326
333
|
}
|
|
327
334
|
case "video": {
|
|
328
335
|
await processVideo();
|
|
329
|
-
result.mime.type = (
|
|
336
|
+
result.mime.type = (_h = result.mime.detected) == null ? void 0 : _h.mime;
|
|
330
337
|
break;
|
|
331
338
|
}
|
|
332
339
|
default: {
|
package/dist/node/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.ts","../../src/Plugin.ts","../../src/Witness/Config.ts","../../src/Witness/Witness.ts","../../src/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.ts"],"sourcesContent":["import { ImageThumbnailPlugin } from './Plugin'\n\nexport * from './Witness'\nexport * from '@xyo-network/diviner-image-thumbnail'\n\nexport { ImageThumbnailPlugin }\n\n// eslint-disable-next-line import/no-default-export\nexport default ImageThumbnailPlugin\n","import { ImageThumbnailDiviner } from '@xyo-network/diviner-image-thumbnail'\nimport { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'\nimport { PayloadSetSchema } from '@xyo-network/payload-model'\nimport { createPayloadSetDualPlugin } from '@xyo-network/payloadset-plugin'\n\nimport { ImageThumbnailWitness } from './Witness'\n\nexport const ImageThumbnailPlugin = () =>\n createPayloadSetDualPlugin<ImageThumbnailWitness, ImageThumbnailDiviner>(\n { required: { [ImageThumbnailSchema]: 1 }, schema: PayloadSetSchema },\n {\n diviner: async (params) => {\n const result = await ImageThumbnailDiviner.create(params)\n return result\n },\n witness: async (params) => {\n const result = await ImageThumbnailWitness.create(params)\n return result\n },\n },\n )\n","import { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'\nimport { WitnessConfig } from '@xyo-network/witness-model'\n\nexport const ImageThumbnailWitnessConfigSchema = `${ImageThumbnailSchema}.witness.config` as const\nexport type ImageThumbnailWitnessConfigSchema = typeof ImageThumbnailWitnessConfigSchema\n\nexport type ImageThumbnailEncoding = 'PNG' | 'JPG' | 'GIF'\n\nexport type ImageThumbnailWitnessConfig = WitnessConfig<{\n encoding?: ImageThumbnailEncoding\n height?: number\n ipfsGateway?: string\n maxAsyncProcesses?: number\n maxCacheBytes?: number\n maxCacheEntries?: number\n quality?: number\n schema: ImageThumbnailWitnessConfigSchema\n width?: number\n}>\n","/* eslint-disable max-statements */\nimport { promises as dnsPromises } from 'node:dns'\n\nimport { compact } from '@xylabs/lodash'\nimport { URL } from '@xylabs/url'\nimport { AbstractWitness } from '@xyo-network/abstract-witness'\nimport { axios, AxiosError, AxiosResponse } from '@xyo-network/axios'\nimport { PayloadHasher } from '@xyo-network/core'\nimport { ImageThumbnail, ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'\nimport { UrlPayload, UrlSchema } from '@xyo-network/url-payload-plugin'\nimport { Semaphore } from 'async-mutex'\nimport FileType from 'file-type'\nimport graphicsMagick from 'gm'\nimport hasbin from 'hasbin'\nimport { sha256 } from 'hash-wasm'\nimport shajs from 'sha.js'\nimport Url from 'url-parse'\n\nimport { ImageThumbnailEncoding, ImageThumbnailWitnessConfigSchema } from './Config'\nimport { getVideoFrameAsImageFluent } from './ffmpeg'\nimport { ImageThumbnailWitnessParams } from './Params'\n\n//TODO: Break this into two Witnesses?\n\n// setFfmpegPath(ffmpegPath)\n\n// eslint-disable-next-line import/no-named-as-default-member\nconst gm = graphicsMagick.subClass({ imageMagick: '7+' })\n\nexport interface ImageThumbnailWitnessError extends Error {\n name: 'ImageThumbnailWitnessError'\n url: string\n}\n\nexport interface DnsError extends Error {\n code: string\n}\n\nexport class ImageThumbnailWitness<TParams extends ImageThumbnailWitnessParams = ImageThumbnailWitnessParams> extends AbstractWitness<TParams> {\n static override configSchemas = [ImageThumbnailWitnessConfigSchema]\n\n private _semaphore = new Semaphore(this.maxAsyncProcesses)\n\n get encoding() {\n return this.config.encoding ?? 'PNG'\n }\n\n get height() {\n return this.config.height ?? 128\n }\n\n get ipfGateway() {\n return this.config.ipfsGateway ?? '5d7b6582.beta.decentralnetworkservices.com'\n }\n\n get maxAsyncProcesses() {\n return this.config.maxAsyncProcesses ?? 2\n }\n\n get quality() {\n return this.config.quality ?? 50\n }\n\n get width() {\n return this.config.width ?? 128\n }\n\n private static async binaryToSha256(data: Uint8Array) {\n await PayloadHasher.wasmInitialized\n if (PayloadHasher.wasmSupport.canUseWasm) {\n try {\n return await sha256(data)\n } catch (ex) {\n PayloadHasher.wasmSupport.allowWasm = false\n }\n }\n\n return shajs('sha256').update(data).digest().toString()\n }\n\n private static bufferFromDataUrl(url: string): Buffer | undefined {\n if (url.startsWith('data:image')) {\n const data = url.split(',')[1]\n if (data) {\n return Buffer.from(Uint8Array.from(atob(data), (c) => c.charCodeAt(0)))\n } else {\n const error: ImageThumbnailWitnessError = {\n message: 'Invalid data Url',\n name: 'ImageThumbnailWitnessError',\n url,\n }\n throw error\n }\n }\n }\n\n /**\n * Returns the equivalent IPFS gateway URL for the supplied URL.\n * @param urlToCheck The URL to check\n * @returns If the supplied URL is an IPFS URL, it converts the URL to the\n * equivalent IPFS gateway URL. Otherwise, returns the original URL.\n */\n checkIpfsUrl(urlToCheck: string) {\n const url = new URL(urlToCheck)\n let protocol = url.protocol\n let host = url.host\n let path = url.pathname\n const query = url.search\n if (protocol === 'ipfs:') {\n protocol = 'https:'\n host = this.ipfGateway\n path = url.host === 'ipfs' ? `ipfs${path}` : `ipfs/${url.host}${path}`\n const root = `${protocol}//${host}/${path}`\n return query?.length > 0 ? `${root}?${query}` : root\n } else {\n return urlToCheck\n }\n }\n\n protected override async observeHandler(payloads: UrlPayload[] = []): Promise<ImageThumbnail[]> {\n // eslint-disable-next-line import/no-named-as-default-member\n if (!hasbin.sync('magick')) {\n throw Error('ImageMagick is required for this witness')\n }\n const urlPayloads = payloads.filter((payload) => payload.schema === UrlSchema)\n return await this._semaphore.runExclusive(async () =>\n compact(\n await Promise.all(\n urlPayloads.map<Promise<ImageThumbnail>>(async ({ url }) => {\n let result: ImageThumbnail\n\n //if it is a data URL, return a Buffer\n const dataBuffer = ImageThumbnailWitness.bufferFromDataUrl(url)\n\n if (dataBuffer) {\n result = {\n schema: ImageThumbnailSchema,\n sourceHash: await ImageThumbnailWitness.binaryToSha256(dataBuffer),\n sourceUrl: url,\n url,\n }\n } else {\n //if it is ipfs, go through cloud flair\n const mutatedUrl = this.checkIpfsUrl(url)\n result = await this.fromHttp(mutatedUrl, url)\n }\n return result\n }),\n ),\n ),\n )\n }\n\n private async createThumbnailDataUrl(sourceBuffer: Buffer, encoding?: ImageThumbnailEncoding) {\n const thumb = await new Promise<Buffer>((resolve, reject) => {\n gm(sourceBuffer)\n .quality(this.quality)\n .resize(this.width, this.height)\n .flatten()\n .toBuffer(encoding ?? this.encoding, (error, buffer) => {\n if (error) {\n reject(error)\n } else {\n resolve(buffer)\n }\n })\n })\n return `data:image/png;base64,${thumb.toString('base64')}`\n }\n\n /**\n * Creates an image thumbnail from a video.\n * @param videoBuffer The input video buffer.\n * @returns An buffer containing an image thumbnail for the video.\n */\n private async createThumbnailFromVideo(videoBuffer: Buffer) {\n const imageBuffer = await getVideoFrameAsImageFluent(videoBuffer)\n return this.createThumbnailDataUrl(imageBuffer)\n }\n\n // eslint-disable-next-line complexity\n private async fromHttp(url: string, sourceUrl?: string): Promise<ImageThumbnail> {\n let response: AxiosResponse\n let dnsResult: string[]\n try {\n const urlObj = new Url(url)\n dnsResult = await dnsPromises.resolve(urlObj.host)\n // console.log(`dnsResult: ${JSON.stringify(dnsResult, null, 2)}`)\n } catch (ex) {\n const error = ex as DnsError\n const result: ImageThumbnail = {\n http: {\n dnsError: error.code,\n },\n schema: ImageThumbnailSchema,\n sourceUrl: sourceUrl ?? url,\n }\n return result\n }\n try {\n response = await axios.get(url, {\n responseType: 'arraybuffer',\n })\n } catch (ex) {\n const axiosError = ex as AxiosError\n if (axiosError.isAxiosError) {\n //selectively pick fields from AxiosError\n const result: ImageThumbnail = {\n http: {\n ipAddress: dnsResult[0],\n status: axiosError?.response?.status,\n },\n schema: ImageThumbnailSchema,\n sourceUrl: sourceUrl ?? url,\n }\n return result\n } else {\n throw ex\n }\n }\n\n const result: ImageThumbnail = {\n http: {\n status: response.status,\n },\n schema: ImageThumbnailSchema,\n sourceUrl: sourceUrl ?? url,\n }\n\n if (response.status >= 200 && response.status < 300) {\n const contentType: string | undefined = response.headers['content-type']?.toString()\n const [mediaType, fileType] = contentType?.split('/') ?? ['', '']\n result.mime = result.mime ?? {}\n result.mime.returned = mediaType\n const sourceBuffer = Buffer.from(response.data, 'binary')\n\n try {\n result.mime.detected = await FileType.fromBuffer(sourceBuffer)\n } catch (ex) {\n const error = ex as Error\n this.logger?.error(`FileType error: ${error.message}`)\n }\n\n const processImage = async (encoding?: ImageThumbnailEncoding) => {\n result.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer)\n result.url = await this.createThumbnailDataUrl(sourceBuffer, encoding)\n }\n\n const processVideo = async () => {\n // Gracefully handle the case where ffmpeg is not installed.\n // eslint-disable-next-line import/no-named-as-default-member\n if (hasbin.sync('ffmpeg')) {\n result.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer)\n result.url = await this.createThumbnailFromVideo(sourceBuffer)\n } else {\n result.mime = result.mime ?? {}\n result.mime.invalid = true\n }\n }\n\n let encoding: ImageThumbnailEncoding = 'PNG'\n\n switch (fileType.toUpperCase()) {\n case 'GIF':\n encoding = 'GIF'\n break\n case 'JPG':\n case 'JPEG':\n encoding = 'JPG'\n break\n }\n\n switch (mediaType) {\n case 'image': {\n await processImage(encoding)\n result.mime.type = mediaType\n break\n }\n case 'video': {\n await processVideo()\n result.mime.type = mediaType\n break\n }\n default: {\n const [detectedMediaType] = result.mime.detected?.mime?.split('/') ?? ['', '']\n switch (detectedMediaType) {\n case 'image': {\n await processImage()\n result.mime.type = result.mime.detected?.mime\n break\n }\n case 'video': {\n await processVideo()\n result.mime.type = result.mime.detected?.mime\n break\n }\n default: {\n result.mime.invalid = true\n break\n }\n }\n break\n }\n }\n }\n return result\n }\n}\n","import { uuid } from '@xyo-network/core'\nimport ffmpeg from 'fluent-ffmpeg'\nimport { unlink, writeFile } from 'fs/promises'\nimport { tmpdir } from 'os'\nimport { Writable, WritableOptions } from 'stream'\n\n/**\n * A Writable stream that collects output from ffmpeg.\n */\nclass FfmpegOutputStream extends Writable {\n private readonly chunks: Uint8Array[] = []\n\n constructor(options?: WritableOptions) {\n super(options)\n }\n\n override _write(chunk: never, _encoding: BufferEncoding, callback: (error?: Error | null) => void): void {\n this.chunks.push(chunk)\n callback()\n }\n\n /**\n * Collects the output from ffmpeg into a buffer.\n * @returns A buffer containing the concatenated\n * output from ffmpeg.\n */\n toBuffer = () => Buffer.concat(this.chunks)\n}\n\n/**\n * Execute FFmpeg using fluent API with provided input buffer and video thumbnail image.\n * @param videoBuffer Input video buffer.\n * @returns Output buffer containing the video thumbnail image.\n */\nexport const getVideoFrameAsImageFluent = async (videoBuffer: Buffer) => {\n // Get a temp file name\n const tmpFile = `/${tmpdir()}/${uuid()}`\n try {\n // Write videoBuffer to temp file for use as input to ffmpeg to\n // avoid issues with ffmpeg inferring premature EOF from buffer\n // passed via stdin (happens when ffmpeg is trying to infer\n // input video format)\n await writeFile(tmpFile, videoBuffer, { encoding: 'binary' })\n const imageBuffer = await new Promise<Buffer>((resolve, reject) => {\n // Create a Writable stream to collect PNG output from ffmpeg\n const ffmpegOutput = new FfmpegOutputStream()\n // Execute ffmpeg using fluent API\n ffmpeg()\n // NOTE: Uncomment to debug CLI args to ffmpeg\n // .on('start', (commandLine) => console.log('Spawned Ffmpeg with command: ' + commandLine))\n .on('error', (err) => reject(err.message))\n // Listen for the 'end' event to combine the output into a buffer holding the PNG image\n .on('end', () => resolve(ffmpegOutput.toBuffer()))\n .input(tmpFile) // Use temp file as input\n .takeFrames(1) // Only take 1st video frame\n .withNoAudio() // Don't include audio\n .outputOptions('-f image2pipe') // Write output to stdout\n .videoCodec('png') // Force PNG output\n // Start processing and direct ffmpeg stdout to writable stream\n .pipe(ffmpegOutput)\n })\n return imageBuffer\n } finally {\n // Cleanup temp file\n try {\n await unlink(tmpFile)\n } catch {\n // No error here since file doesn't exist\n }\n }\n}\n"],"mappings":"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","names":["import_image_thumbnail_payload_plugin","import_core","import_image_thumbnail_payload_plugin","ffmpeg","graphicsMagick","shajs","hasbin","Url","dnsPromises","result","FileType","encoding"]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts","../../src/Plugin.ts","../../src/Witness/Config.ts","../../src/Witness/Witness.ts","../../src/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.ts"],"sourcesContent":["import { ImageThumbnailPlugin } from './Plugin'\n\nexport * from './Witness'\nexport * from '@xyo-network/diviner-image-thumbnail'\n\nexport { ImageThumbnailPlugin }\n\n// eslint-disable-next-line import/no-default-export\nexport default ImageThumbnailPlugin\n","import { ImageThumbnailDiviner } from '@xyo-network/diviner-image-thumbnail'\nimport { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'\nimport { PayloadSetSchema } from '@xyo-network/payload-model'\nimport { createPayloadSetDualPlugin } from '@xyo-network/payloadset-plugin'\n\nimport { ImageThumbnailWitness } from './Witness'\n\nexport const ImageThumbnailPlugin = () =>\n createPayloadSetDualPlugin<ImageThumbnailWitness, ImageThumbnailDiviner>(\n { required: { [ImageThumbnailSchema]: 1 }, schema: PayloadSetSchema },\n {\n diviner: async (params) => {\n const result = await ImageThumbnailDiviner.create(params)\n return result\n },\n witness: async (params) => {\n const result = await ImageThumbnailWitness.create(params)\n return result\n },\n },\n )\n","import { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'\nimport { WitnessConfig } from '@xyo-network/witness-model'\n\nexport const ImageThumbnailWitnessConfigSchema = `${ImageThumbnailSchema}.witness.config` as const\nexport type ImageThumbnailWitnessConfigSchema = typeof ImageThumbnailWitnessConfigSchema\n\nexport type ImageThumbnailEncoding = 'PNG' | 'JPG' | 'GIF'\n\nexport type ImageThumbnailWitnessConfig = WitnessConfig<{\n encoding?: ImageThumbnailEncoding\n height?: number\n ipfsGateway?: string\n maxAsyncProcesses?: number\n maxCacheBytes?: number\n maxCacheEntries?: number\n quality?: number\n schema: ImageThumbnailWitnessConfigSchema\n width?: number\n}>\n","/* eslint-disable max-statements */\nimport { promises as dnsPromises } from 'node:dns'\n\nimport { compact } from '@xylabs/lodash'\nimport { URL } from '@xylabs/url'\nimport { AbstractWitness } from '@xyo-network/abstract-witness'\nimport { axios, AxiosError, AxiosResponse } from '@xyo-network/axios'\nimport { PayloadHasher } from '@xyo-network/core'\nimport { ImageThumbnail, ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'\nimport { UrlPayload, UrlSchema } from '@xyo-network/url-payload-plugin'\nimport { Semaphore } from 'async-mutex'\nimport FileType from 'file-type'\nimport graphicsMagick from 'gm'\nimport hasbin from 'hasbin'\nimport { sha256 } from 'hash-wasm'\nimport shajs from 'sha.js'\nimport Url from 'url-parse'\n\nimport { ImageThumbnailEncoding, ImageThumbnailWitnessConfigSchema } from './Config'\nimport { getVideoFrameAsImageFluent } from './ffmpeg'\nimport { ImageThumbnailWitnessParams } from './Params'\n\n//TODO: Break this into two Witnesses?\n\n// setFfmpegPath(ffmpegPath)\n\n// eslint-disable-next-line import/no-named-as-default-member\nconst gm = graphicsMagick.subClass({ imageMagick: '7+' })\n\nexport interface ImageThumbnailWitnessError extends Error {\n name: 'ImageThumbnailWitnessError'\n url: string\n}\n\nexport interface DnsError extends Error {\n code: string\n}\n\nexport class ImageThumbnailWitness<TParams extends ImageThumbnailWitnessParams = ImageThumbnailWitnessParams> extends AbstractWitness<TParams> {\n static override configSchemas = [ImageThumbnailWitnessConfigSchema]\n\n private _semaphore = new Semaphore(this.maxAsyncProcesses)\n\n get encoding() {\n return this.config.encoding ?? 'PNG'\n }\n\n get height() {\n return this.config.height ?? 128\n }\n\n get ipfGateway() {\n return this.config.ipfsGateway ?? '5d7b6582.beta.decentralnetworkservices.com'\n }\n\n get maxAsyncProcesses() {\n return this.config.maxAsyncProcesses ?? 2\n }\n\n get quality() {\n return this.config.quality ?? 50\n }\n\n get width() {\n return this.config.width ?? 128\n }\n\n private static async binaryToSha256(data: Uint8Array) {\n await PayloadHasher.wasmInitialized\n if (PayloadHasher.wasmSupport.canUseWasm) {\n try {\n return await sha256(data)\n } catch (ex) {\n PayloadHasher.wasmSupport.allowWasm = false\n }\n }\n\n return shajs('sha256').update(data).digest().toString()\n }\n\n private static bufferFromDataUrl(url: string): Buffer | undefined {\n if (url.startsWith('data:image')) {\n const data = url.split(',')[1]\n if (data) {\n return Buffer.from(Uint8Array.from(atob(data), (c) => c.charCodeAt(0)))\n } else {\n const error: ImageThumbnailWitnessError = {\n message: 'Invalid data Url',\n name: 'ImageThumbnailWitnessError',\n url,\n }\n throw error\n }\n }\n }\n\n /**\n * Returns the equivalent IPFS gateway URL for the supplied URL.\n * @param urlToCheck The URL to check\n * @returns If the supplied URL is an IPFS URL, it converts the URL to the\n * equivalent IPFS gateway URL. Otherwise, returns the original URL.\n */\n checkIpfsUrl(urlToCheck: string) {\n const url = new URL(urlToCheck)\n let protocol = url.protocol\n let host = url.host\n let path = url.pathname\n const query = url.search\n if (protocol === 'ipfs:') {\n protocol = 'https:'\n host = this.ipfGateway\n path = url.host === 'ipfs' ? `ipfs${path}` : `ipfs/${url.host}${path}`\n const root = `${protocol}//${host}/${path}`\n return query?.length > 0 ? `${root}?${query}` : root\n } else {\n return urlToCheck\n }\n }\n\n protected override async observeHandler(payloads: UrlPayload[] = []): Promise<ImageThumbnail[]> {\n // eslint-disable-next-line import/no-named-as-default-member\n if (!hasbin.sync('magick')) {\n throw Error('ImageMagick is required for this witness')\n }\n const urlPayloads = payloads.filter((payload) => payload.schema === UrlSchema)\n return await this._semaphore.runExclusive(async () =>\n compact(\n await Promise.all(\n urlPayloads.map<Promise<ImageThumbnail>>(async ({ url }) => {\n let result: ImageThumbnail\n\n //if it is a data URL, return a Buffer\n const dataBuffer = ImageThumbnailWitness.bufferFromDataUrl(url)\n\n if (dataBuffer) {\n result = {\n schema: ImageThumbnailSchema,\n sourceHash: await ImageThumbnailWitness.binaryToSha256(dataBuffer),\n sourceUrl: url,\n url,\n }\n } else {\n //if it is ipfs, go through cloud flair\n const mutatedUrl = this.checkIpfsUrl(url)\n result = await this.fromHttp(mutatedUrl, url)\n }\n return result\n }),\n ),\n ),\n )\n }\n\n private async createThumbnailDataUrl(sourceBuffer: Buffer, encoding?: ImageThumbnailEncoding) {\n const thumb = await new Promise<Buffer>((resolve, reject) => {\n gm(sourceBuffer)\n .quality(this.quality)\n .resize(this.width, this.height)\n .flatten()\n .toBuffer(encoding ?? this.encoding, (error, buffer) => {\n if (error) {\n reject(error)\n } else {\n resolve(buffer)\n }\n })\n })\n return `data:image/png;base64,${thumb.toString('base64')}`\n }\n\n /**\n * Creates an image thumbnail from a video.\n * @param videoBuffer The input video buffer.\n * @returns An buffer containing an image thumbnail for the video.\n */\n private async createThumbnailFromVideo(videoBuffer: Buffer) {\n const imageBuffer = await getVideoFrameAsImageFluent(videoBuffer)\n return this.createThumbnailDataUrl(imageBuffer)\n }\n\n // eslint-disable-next-line complexity\n private async fromHttp(url: string, sourceUrl?: string): Promise<ImageThumbnail> {\n let response: AxiosResponse\n let dnsResult: string[]\n try {\n const urlObj = new Url(url)\n dnsResult = await dnsPromises.resolve(urlObj.host)\n // console.log(`dnsResult: ${JSON.stringify(dnsResult, null, 2)}`)\n } catch (ex) {\n const error = ex as DnsError\n const result: ImageThumbnail = {\n http: {\n code: error.code,\n },\n schema: ImageThumbnailSchema,\n sourceUrl: sourceUrl ?? url,\n }\n return result\n }\n try {\n response = await axios.get(url, {\n responseType: 'arraybuffer',\n })\n } catch (ex) {\n const axiosError = ex as AxiosError\n if (axiosError.isAxiosError) {\n //selectively pick fields from AxiosError\n const result: ImageThumbnail = {\n http: {\n ipAddress: dnsResult[0],\n },\n schema: ImageThumbnailSchema,\n sourceUrl: sourceUrl ?? url,\n }\n if (axiosError?.response?.status !== undefined) {\n result.http = result.http ?? {}\n result.http.status = axiosError?.response?.status\n }\n if (axiosError?.code !== undefined) {\n result.http = result.http ?? {}\n result.http.code = axiosError?.code\n }\n return result\n } else {\n throw ex\n }\n }\n\n const result: ImageThumbnail = {\n http: {\n status: response.status,\n },\n schema: ImageThumbnailSchema,\n sourceUrl: sourceUrl ?? url,\n }\n\n if (response.status >= 200 && response.status < 300) {\n const contentType: string | undefined = response.headers['content-type']?.toString()\n const [mediaType, fileType] = contentType?.split('/') ?? ['', '']\n result.mime = result.mime ?? {}\n result.mime.returned = mediaType\n const sourceBuffer = Buffer.from(response.data, 'binary')\n\n try {\n result.mime.detected = await FileType.fromBuffer(sourceBuffer)\n } catch (ex) {\n const error = ex as Error\n this.logger?.error(`FileType error: ${error.message}`)\n }\n\n const processImage = async (encoding?: ImageThumbnailEncoding) => {\n result.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer)\n result.url = await this.createThumbnailDataUrl(sourceBuffer, encoding)\n }\n\n const processVideo = async () => {\n // Gracefully handle the case where ffmpeg is not installed.\n // eslint-disable-next-line import/no-named-as-default-member\n if (hasbin.sync('ffmpeg')) {\n result.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer)\n result.url = await this.createThumbnailFromVideo(sourceBuffer)\n } else {\n result.mime = result.mime ?? {}\n result.mime.invalid = true\n }\n }\n\n let encoding: ImageThumbnailEncoding = 'PNG'\n\n switch (fileType.toUpperCase()) {\n case 'GIF':\n encoding = 'GIF'\n break\n case 'JPG':\n case 'JPEG':\n encoding = 'JPG'\n break\n }\n\n switch (mediaType) {\n case 'image': {\n await processImage(encoding)\n result.mime.type = mediaType\n break\n }\n case 'video': {\n await processVideo()\n result.mime.type = mediaType\n break\n }\n default: {\n const [detectedMediaType] = result.mime.detected?.mime?.split('/') ?? ['', '']\n switch (detectedMediaType) {\n case 'image': {\n await processImage()\n result.mime.type = result.mime.detected?.mime\n break\n }\n case 'video': {\n await processVideo()\n result.mime.type = result.mime.detected?.mime\n break\n }\n default: {\n result.mime.invalid = true\n break\n }\n }\n break\n }\n }\n }\n return result\n }\n}\n","import { uuid } from '@xyo-network/core'\nimport ffmpeg from 'fluent-ffmpeg'\nimport { unlink, writeFile } from 'fs/promises'\nimport { tmpdir } from 'os'\nimport { Writable, WritableOptions } from 'stream'\n\n/**\n * A Writable stream that collects output from ffmpeg.\n */\nclass FfmpegOutputStream extends Writable {\n private readonly chunks: Uint8Array[] = []\n\n constructor(options?: WritableOptions) {\n super(options)\n }\n\n override _write(chunk: never, _encoding: BufferEncoding, callback: (error?: Error | null) => void): void {\n this.chunks.push(chunk)\n callback()\n }\n\n /**\n * Collects the output from ffmpeg into a buffer.\n * @returns A buffer containing the concatenated\n * output from ffmpeg.\n */\n toBuffer = () => Buffer.concat(this.chunks)\n}\n\n/**\n * Execute FFmpeg using fluent API with provided input buffer and video thumbnail image.\n * @param videoBuffer Input video buffer.\n * @returns Output buffer containing the video thumbnail image.\n */\nexport const getVideoFrameAsImageFluent = async (videoBuffer: Buffer) => {\n // Get a temp file name\n const tmpFile = `/${tmpdir()}/${uuid()}`\n try {\n // Write videoBuffer to temp file for use as input to ffmpeg to\n // avoid issues with ffmpeg inferring premature EOF from buffer\n // passed via stdin (happens when ffmpeg is trying to infer\n // input video format)\n await writeFile(tmpFile, videoBuffer, { encoding: 'binary' })\n const imageBuffer = await new Promise<Buffer>((resolve, reject) => {\n // Create a Writable stream to collect PNG output from ffmpeg\n const ffmpegOutput = new FfmpegOutputStream()\n // Execute ffmpeg using fluent API\n ffmpeg()\n // NOTE: Uncomment to debug CLI args to ffmpeg\n // .on('start', (commandLine) => console.log('Spawned Ffmpeg with command: ' + commandLine))\n .on('error', (err) => reject(err.message))\n // Listen for the 'end' event to combine the output into a buffer holding the PNG image\n .on('end', () => resolve(ffmpegOutput.toBuffer()))\n .input(tmpFile) // Use temp file as input\n .takeFrames(1) // Only take 1st video frame\n .withNoAudio() // Don't include audio\n .outputOptions('-f image2pipe') // Write output to stdout\n .videoCodec('png') // Force PNG output\n // Start processing and direct ffmpeg stdout to writable stream\n .pipe(ffmpegOutput)\n })\n return imageBuffer\n } finally {\n // Cleanup temp file\n try {\n await unlink(tmpFile)\n } catch {\n // No error here since file doesn't exist\n }\n }\n}\n"],"mappings":"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","names":["import_image_thumbnail_payload_plugin","import_core","import_image_thumbnail_payload_plugin","ffmpeg","graphicsMagick","shajs","hasbin","Url","dnsPromises","result","FileType","encoding"]}
|
package/dist/node/index.mjs
CHANGED
|
@@ -186,7 +186,7 @@ var ImageThumbnailWitness = class _ImageThumbnailWitness extends AbstractWitness
|
|
|
186
186
|
}
|
|
187
187
|
// eslint-disable-next-line complexity
|
|
188
188
|
async fromHttp(url, sourceUrl) {
|
|
189
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
189
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
190
190
|
let response;
|
|
191
191
|
let dnsResult;
|
|
192
192
|
try {
|
|
@@ -196,7 +196,7 @@ var ImageThumbnailWitness = class _ImageThumbnailWitness extends AbstractWitness
|
|
|
196
196
|
const error = ex;
|
|
197
197
|
const result2 = {
|
|
198
198
|
http: {
|
|
199
|
-
|
|
199
|
+
code: error.code
|
|
200
200
|
},
|
|
201
201
|
schema: ImageThumbnailSchema2,
|
|
202
202
|
sourceUrl: sourceUrl ?? url
|
|
@@ -212,12 +212,19 @@ var ImageThumbnailWitness = class _ImageThumbnailWitness extends AbstractWitness
|
|
|
212
212
|
if (axiosError.isAxiosError) {
|
|
213
213
|
const result2 = {
|
|
214
214
|
http: {
|
|
215
|
-
ipAddress: dnsResult[0]
|
|
216
|
-
status: (_a = axiosError == null ? void 0 : axiosError.response) == null ? void 0 : _a.status
|
|
215
|
+
ipAddress: dnsResult[0]
|
|
217
216
|
},
|
|
218
217
|
schema: ImageThumbnailSchema2,
|
|
219
218
|
sourceUrl: sourceUrl ?? url
|
|
220
219
|
};
|
|
220
|
+
if (((_a = axiosError == null ? void 0 : axiosError.response) == null ? void 0 : _a.status) !== void 0) {
|
|
221
|
+
result2.http = result2.http ?? {};
|
|
222
|
+
result2.http.status = (_b = axiosError == null ? void 0 : axiosError.response) == null ? void 0 : _b.status;
|
|
223
|
+
}
|
|
224
|
+
if ((axiosError == null ? void 0 : axiosError.code) !== void 0) {
|
|
225
|
+
result2.http = result2.http ?? {};
|
|
226
|
+
result2.http.code = axiosError == null ? void 0 : axiosError.code;
|
|
227
|
+
}
|
|
221
228
|
return result2;
|
|
222
229
|
} else {
|
|
223
230
|
throw ex;
|
|
@@ -231,7 +238,7 @@ var ImageThumbnailWitness = class _ImageThumbnailWitness extends AbstractWitness
|
|
|
231
238
|
sourceUrl: sourceUrl ?? url
|
|
232
239
|
};
|
|
233
240
|
if (response.status >= 200 && response.status < 300) {
|
|
234
|
-
const contentType = (
|
|
241
|
+
const contentType = (_c = response.headers["content-type"]) == null ? void 0 : _c.toString();
|
|
235
242
|
const [mediaType, fileType] = (contentType == null ? void 0 : contentType.split("/")) ?? ["", ""];
|
|
236
243
|
result.mime = result.mime ?? {};
|
|
237
244
|
result.mime.returned = mediaType;
|
|
@@ -240,7 +247,7 @@ var ImageThumbnailWitness = class _ImageThumbnailWitness extends AbstractWitness
|
|
|
240
247
|
result.mime.detected = await FileType.fromBuffer(sourceBuffer);
|
|
241
248
|
} catch (ex) {
|
|
242
249
|
const error = ex;
|
|
243
|
-
(
|
|
250
|
+
(_d = this.logger) == null ? void 0 : _d.error(`FileType error: ${error.message}`);
|
|
244
251
|
}
|
|
245
252
|
const processImage = async (encoding2) => {
|
|
246
253
|
result.sourceHash = await _ImageThumbnailWitness.binaryToSha256(sourceBuffer);
|
|
@@ -277,16 +284,16 @@ var ImageThumbnailWitness = class _ImageThumbnailWitness extends AbstractWitness
|
|
|
277
284
|
break;
|
|
278
285
|
}
|
|
279
286
|
default: {
|
|
280
|
-
const [detectedMediaType] = ((
|
|
287
|
+
const [detectedMediaType] = ((_f = (_e = result.mime.detected) == null ? void 0 : _e.mime) == null ? void 0 : _f.split("/")) ?? ["", ""];
|
|
281
288
|
switch (detectedMediaType) {
|
|
282
289
|
case "image": {
|
|
283
290
|
await processImage();
|
|
284
|
-
result.mime.type = (
|
|
291
|
+
result.mime.type = (_g = result.mime.detected) == null ? void 0 : _g.mime;
|
|
285
292
|
break;
|
|
286
293
|
}
|
|
287
294
|
case "video": {
|
|
288
295
|
await processVideo();
|
|
289
|
-
result.mime.type = (
|
|
296
|
+
result.mime.type = (_h = result.mime.detected) == null ? void 0 : _h.mime;
|
|
290
297
|
break;
|
|
291
298
|
}
|
|
292
299
|
default: {
|
package/dist/node/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/Plugin.ts","../../src/Witness/Config.ts","../../src/Witness/Witness.ts","../../src/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.ts","../../src/index.ts"],"sourcesContent":["import { ImageThumbnailDiviner } from '@xyo-network/diviner-image-thumbnail'\nimport { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'\nimport { PayloadSetSchema } from '@xyo-network/payload-model'\nimport { createPayloadSetDualPlugin } from '@xyo-network/payloadset-plugin'\n\nimport { ImageThumbnailWitness } from './Witness'\n\nexport const ImageThumbnailPlugin = () =>\n createPayloadSetDualPlugin<ImageThumbnailWitness, ImageThumbnailDiviner>(\n { required: { [ImageThumbnailSchema]: 1 }, schema: PayloadSetSchema },\n {\n diviner: async (params) => {\n const result = await ImageThumbnailDiviner.create(params)\n return result\n },\n witness: async (params) => {\n const result = await ImageThumbnailWitness.create(params)\n return result\n },\n },\n )\n","import { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'\nimport { WitnessConfig } from '@xyo-network/witness-model'\n\nexport const ImageThumbnailWitnessConfigSchema = `${ImageThumbnailSchema}.witness.config` as const\nexport type ImageThumbnailWitnessConfigSchema = typeof ImageThumbnailWitnessConfigSchema\n\nexport type ImageThumbnailEncoding = 'PNG' | 'JPG' | 'GIF'\n\nexport type ImageThumbnailWitnessConfig = WitnessConfig<{\n encoding?: ImageThumbnailEncoding\n height?: number\n ipfsGateway?: string\n maxAsyncProcesses?: number\n maxCacheBytes?: number\n maxCacheEntries?: number\n quality?: number\n schema: ImageThumbnailWitnessConfigSchema\n width?: number\n}>\n","/* eslint-disable max-statements */\nimport { promises as dnsPromises } from 'node:dns'\n\nimport { compact } from '@xylabs/lodash'\nimport { URL } from '@xylabs/url'\nimport { AbstractWitness } from '@xyo-network/abstract-witness'\nimport { axios, AxiosError, AxiosResponse } from '@xyo-network/axios'\nimport { PayloadHasher } from '@xyo-network/core'\nimport { ImageThumbnail, ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'\nimport { UrlPayload, UrlSchema } from '@xyo-network/url-payload-plugin'\nimport { Semaphore } from 'async-mutex'\nimport FileType from 'file-type'\nimport graphicsMagick from 'gm'\nimport hasbin from 'hasbin'\nimport { sha256 } from 'hash-wasm'\nimport shajs from 'sha.js'\nimport Url from 'url-parse'\n\nimport { ImageThumbnailEncoding, ImageThumbnailWitnessConfigSchema } from './Config'\nimport { getVideoFrameAsImageFluent } from './ffmpeg'\nimport { ImageThumbnailWitnessParams } from './Params'\n\n//TODO: Break this into two Witnesses?\n\n// setFfmpegPath(ffmpegPath)\n\n// eslint-disable-next-line import/no-named-as-default-member\nconst gm = graphicsMagick.subClass({ imageMagick: '7+' })\n\nexport interface ImageThumbnailWitnessError extends Error {\n name: 'ImageThumbnailWitnessError'\n url: string\n}\n\nexport interface DnsError extends Error {\n code: string\n}\n\nexport class ImageThumbnailWitness<TParams extends ImageThumbnailWitnessParams = ImageThumbnailWitnessParams> extends AbstractWitness<TParams> {\n static override configSchemas = [ImageThumbnailWitnessConfigSchema]\n\n private _semaphore = new Semaphore(this.maxAsyncProcesses)\n\n get encoding() {\n return this.config.encoding ?? 'PNG'\n }\n\n get height() {\n return this.config.height ?? 128\n }\n\n get ipfGateway() {\n return this.config.ipfsGateway ?? '5d7b6582.beta.decentralnetworkservices.com'\n }\n\n get maxAsyncProcesses() {\n return this.config.maxAsyncProcesses ?? 2\n }\n\n get quality() {\n return this.config.quality ?? 50\n }\n\n get width() {\n return this.config.width ?? 128\n }\n\n private static async binaryToSha256(data: Uint8Array) {\n await PayloadHasher.wasmInitialized\n if (PayloadHasher.wasmSupport.canUseWasm) {\n try {\n return await sha256(data)\n } catch (ex) {\n PayloadHasher.wasmSupport.allowWasm = false\n }\n }\n\n return shajs('sha256').update(data).digest().toString()\n }\n\n private static bufferFromDataUrl(url: string): Buffer | undefined {\n if (url.startsWith('data:image')) {\n const data = url.split(',')[1]\n if (data) {\n return Buffer.from(Uint8Array.from(atob(data), (c) => c.charCodeAt(0)))\n } else {\n const error: ImageThumbnailWitnessError = {\n message: 'Invalid data Url',\n name: 'ImageThumbnailWitnessError',\n url,\n }\n throw error\n }\n }\n }\n\n /**\n * Returns the equivalent IPFS gateway URL for the supplied URL.\n * @param urlToCheck The URL to check\n * @returns If the supplied URL is an IPFS URL, it converts the URL to the\n * equivalent IPFS gateway URL. Otherwise, returns the original URL.\n */\n checkIpfsUrl(urlToCheck: string) {\n const url = new URL(urlToCheck)\n let protocol = url.protocol\n let host = url.host\n let path = url.pathname\n const query = url.search\n if (protocol === 'ipfs:') {\n protocol = 'https:'\n host = this.ipfGateway\n path = url.host === 'ipfs' ? `ipfs${path}` : `ipfs/${url.host}${path}`\n const root = `${protocol}//${host}/${path}`\n return query?.length > 0 ? `${root}?${query}` : root\n } else {\n return urlToCheck\n }\n }\n\n protected override async observeHandler(payloads: UrlPayload[] = []): Promise<ImageThumbnail[]> {\n // eslint-disable-next-line import/no-named-as-default-member\n if (!hasbin.sync('magick')) {\n throw Error('ImageMagick is required for this witness')\n }\n const urlPayloads = payloads.filter((payload) => payload.schema === UrlSchema)\n return await this._semaphore.runExclusive(async () =>\n compact(\n await Promise.all(\n urlPayloads.map<Promise<ImageThumbnail>>(async ({ url }) => {\n let result: ImageThumbnail\n\n //if it is a data URL, return a Buffer\n const dataBuffer = ImageThumbnailWitness.bufferFromDataUrl(url)\n\n if (dataBuffer) {\n result = {\n schema: ImageThumbnailSchema,\n sourceHash: await ImageThumbnailWitness.binaryToSha256(dataBuffer),\n sourceUrl: url,\n url,\n }\n } else {\n //if it is ipfs, go through cloud flair\n const mutatedUrl = this.checkIpfsUrl(url)\n result = await this.fromHttp(mutatedUrl, url)\n }\n return result\n }),\n ),\n ),\n )\n }\n\n private async createThumbnailDataUrl(sourceBuffer: Buffer, encoding?: ImageThumbnailEncoding) {\n const thumb = await new Promise<Buffer>((resolve, reject) => {\n gm(sourceBuffer)\n .quality(this.quality)\n .resize(this.width, this.height)\n .flatten()\n .toBuffer(encoding ?? this.encoding, (error, buffer) => {\n if (error) {\n reject(error)\n } else {\n resolve(buffer)\n }\n })\n })\n return `data:image/png;base64,${thumb.toString('base64')}`\n }\n\n /**\n * Creates an image thumbnail from a video.\n * @param videoBuffer The input video buffer.\n * @returns An buffer containing an image thumbnail for the video.\n */\n private async createThumbnailFromVideo(videoBuffer: Buffer) {\n const imageBuffer = await getVideoFrameAsImageFluent(videoBuffer)\n return this.createThumbnailDataUrl(imageBuffer)\n }\n\n // eslint-disable-next-line complexity\n private async fromHttp(url: string, sourceUrl?: string): Promise<ImageThumbnail> {\n let response: AxiosResponse\n let dnsResult: string[]\n try {\n const urlObj = new Url(url)\n dnsResult = await dnsPromises.resolve(urlObj.host)\n // console.log(`dnsResult: ${JSON.stringify(dnsResult, null, 2)}`)\n } catch (ex) {\n const error = ex as DnsError\n const result: ImageThumbnail = {\n http: {\n dnsError: error.code,\n },\n schema: ImageThumbnailSchema,\n sourceUrl: sourceUrl ?? url,\n }\n return result\n }\n try {\n response = await axios.get(url, {\n responseType: 'arraybuffer',\n })\n } catch (ex) {\n const axiosError = ex as AxiosError\n if (axiosError.isAxiosError) {\n //selectively pick fields from AxiosError\n const result: ImageThumbnail = {\n http: {\n ipAddress: dnsResult[0],\n status: axiosError?.response?.status,\n },\n schema: ImageThumbnailSchema,\n sourceUrl: sourceUrl ?? url,\n }\n return result\n } else {\n throw ex\n }\n }\n\n const result: ImageThumbnail = {\n http: {\n status: response.status,\n },\n schema: ImageThumbnailSchema,\n sourceUrl: sourceUrl ?? url,\n }\n\n if (response.status >= 200 && response.status < 300) {\n const contentType: string | undefined = response.headers['content-type']?.toString()\n const [mediaType, fileType] = contentType?.split('/') ?? ['', '']\n result.mime = result.mime ?? {}\n result.mime.returned = mediaType\n const sourceBuffer = Buffer.from(response.data, 'binary')\n\n try {\n result.mime.detected = await FileType.fromBuffer(sourceBuffer)\n } catch (ex) {\n const error = ex as Error\n this.logger?.error(`FileType error: ${error.message}`)\n }\n\n const processImage = async (encoding?: ImageThumbnailEncoding) => {\n result.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer)\n result.url = await this.createThumbnailDataUrl(sourceBuffer, encoding)\n }\n\n const processVideo = async () => {\n // Gracefully handle the case where ffmpeg is not installed.\n // eslint-disable-next-line import/no-named-as-default-member\n if (hasbin.sync('ffmpeg')) {\n result.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer)\n result.url = await this.createThumbnailFromVideo(sourceBuffer)\n } else {\n result.mime = result.mime ?? {}\n result.mime.invalid = true\n }\n }\n\n let encoding: ImageThumbnailEncoding = 'PNG'\n\n switch (fileType.toUpperCase()) {\n case 'GIF':\n encoding = 'GIF'\n break\n case 'JPG':\n case 'JPEG':\n encoding = 'JPG'\n break\n }\n\n switch (mediaType) {\n case 'image': {\n await processImage(encoding)\n result.mime.type = mediaType\n break\n }\n case 'video': {\n await processVideo()\n result.mime.type = mediaType\n break\n }\n default: {\n const [detectedMediaType] = result.mime.detected?.mime?.split('/') ?? ['', '']\n switch (detectedMediaType) {\n case 'image': {\n await processImage()\n result.mime.type = result.mime.detected?.mime\n break\n }\n case 'video': {\n await processVideo()\n result.mime.type = result.mime.detected?.mime\n break\n }\n default: {\n result.mime.invalid = true\n break\n }\n }\n break\n }\n }\n }\n return result\n }\n}\n","import { uuid } from '@xyo-network/core'\nimport ffmpeg from 'fluent-ffmpeg'\nimport { unlink, writeFile } from 'fs/promises'\nimport { tmpdir } from 'os'\nimport { Writable, WritableOptions } from 'stream'\n\n/**\n * A Writable stream that collects output from ffmpeg.\n */\nclass FfmpegOutputStream extends Writable {\n private readonly chunks: Uint8Array[] = []\n\n constructor(options?: WritableOptions) {\n super(options)\n }\n\n override _write(chunk: never, _encoding: BufferEncoding, callback: (error?: Error | null) => void): void {\n this.chunks.push(chunk)\n callback()\n }\n\n /**\n * Collects the output from ffmpeg into a buffer.\n * @returns A buffer containing the concatenated\n * output from ffmpeg.\n */\n toBuffer = () => Buffer.concat(this.chunks)\n}\n\n/**\n * Execute FFmpeg using fluent API with provided input buffer and video thumbnail image.\n * @param videoBuffer Input video buffer.\n * @returns Output buffer containing the video thumbnail image.\n */\nexport const getVideoFrameAsImageFluent = async (videoBuffer: Buffer) => {\n // Get a temp file name\n const tmpFile = `/${tmpdir()}/${uuid()}`\n try {\n // Write videoBuffer to temp file for use as input to ffmpeg to\n // avoid issues with ffmpeg inferring premature EOF from buffer\n // passed via stdin (happens when ffmpeg is trying to infer\n // input video format)\n await writeFile(tmpFile, videoBuffer, { encoding: 'binary' })\n const imageBuffer = await new Promise<Buffer>((resolve, reject) => {\n // Create a Writable stream to collect PNG output from ffmpeg\n const ffmpegOutput = new FfmpegOutputStream()\n // Execute ffmpeg using fluent API\n ffmpeg()\n // NOTE: Uncomment to debug CLI args to ffmpeg\n // .on('start', (commandLine) => console.log('Spawned Ffmpeg with command: ' + commandLine))\n .on('error', (err) => reject(err.message))\n // Listen for the 'end' event to combine the output into a buffer holding the PNG image\n .on('end', () => resolve(ffmpegOutput.toBuffer()))\n .input(tmpFile) // Use temp file as input\n .takeFrames(1) // Only take 1st video frame\n .withNoAudio() // Don't include audio\n .outputOptions('-f image2pipe') // Write output to stdout\n .videoCodec('png') // Force PNG output\n // Start processing and direct ffmpeg stdout to writable stream\n .pipe(ffmpegOutput)\n })\n return imageBuffer\n } finally {\n // Cleanup temp file\n try {\n await unlink(tmpFile)\n } catch {\n // No error here since file doesn't exist\n }\n }\n}\n","import { ImageThumbnailPlugin } from './Plugin'\n\nexport * from './Witness'\nexport * from '@xyo-network/diviner-image-thumbnail'\n\nexport { ImageThumbnailPlugin }\n\n// eslint-disable-next-line import/no-default-export\nexport default ImageThumbnailPlugin\n"],"mappings":";AAAA,SAAS,6BAA6B;AACtC,SAAS,wBAAAA,6BAA4B;AACrC,SAAS,wBAAwB;AACjC,SAAS,kCAAkC;;;ACH3C,SAAS,4BAA4B;AAG9B,IAAM,oCAAoC,GAAG,oBAAoB;;;ACFxE,SAAS,YAAY,mBAAmB;AAExC,SAAS,eAAe;AACxB,SAAS,WAAW;AACpB,SAAS,uBAAuB;AAChC,SAAS,aAAwC;AACjD,SAAS,qBAAqB;AAC9B,SAAyB,wBAAAC,6BAA4B;AACrD,SAAqB,iBAAiB;AACtC,SAAS,iBAAiB;AAC1B,OAAO,cAAc;AACrB,OAAO,oBAAoB;AAC3B,OAAO,YAAY;AACnB,SAAS,cAAc;AACvB,OAAO,WAAW;AAClB,OAAO,SAAS;;;AChBhB,SAAS,YAAY;AACrB,OAAO,YAAY;AACnB,SAAS,QAAQ,iBAAiB;AAClC,SAAS,cAAc;AACvB,SAAS,gBAAiC;AAK1C,IAAM,qBAAN,cAAiC,SAAS;AAAA,EACvB,SAAuB,CAAC;AAAA,EAEzC,YAAY,SAA2B;AACrC,UAAM,OAAO;AAAA,EACf;AAAA,EAES,OAAO,OAAc,WAA2B,UAAgD;AACvG,SAAK,OAAO,KAAK,KAAK;AACtB,aAAS;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAW,MAAM,OAAO,OAAO,KAAK,MAAM;AAC5C;AAOO,IAAM,6BAA6B,OAAO,gBAAwB;AAEvE,QAAM,UAAU,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC;AACtC,MAAI;AAKF,UAAM,UAAU,SAAS,aAAa,EAAE,UAAU,SAAS,CAAC;AAC5D,UAAM,cAAc,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AAEjE,YAAM,eAAe,IAAI,mBAAmB;AAE5C,aAAO,EAGJ,GAAG,SAAS,CAAC,QAAQ,OAAO,IAAI,OAAO,CAAC,EAExC,GAAG,OAAO,MAAM,QAAQ,aAAa,SAAS,CAAC,CAAC,EAChD,MAAM,OAAO,EACb,WAAW,CAAC,EACZ,YAAY,EACZ,cAAc,eAAe,EAC7B,WAAW,KAAK,EAEhB,KAAK,YAAY;AAAA,IACtB,CAAC;AACD,WAAO;AAAA,EACT,UAAE;AAEA,QAAI;AACF,YAAM,OAAO,OAAO;AAAA,IACtB,QAAQ;AAAA,IAER;AAAA,EACF;AACF;;;AD3CA,IAAM,KAAK,eAAe,SAAS,EAAE,aAAa,KAAK,CAAC;AAWjD,IAAM,wBAAN,MAAM,+BAAyG,gBAAyB;AAAA,EAC7I,OAAgB,gBAAgB,CAAC,iCAAiC;AAAA,EAE1D,aAAa,IAAI,UAAU,KAAK,iBAAiB;AAAA,EAEzD,IAAI,WAAW;AACb,WAAO,KAAK,OAAO,YAAY;AAAA,EACjC;AAAA,EAEA,IAAI,SAAS;AACX,WAAO,KAAK,OAAO,UAAU;AAAA,EAC/B;AAAA,EAEA,IAAI,aAAa;AACf,WAAO,KAAK,OAAO,eAAe;AAAA,EACpC;AAAA,EAEA,IAAI,oBAAoB;AACtB,WAAO,KAAK,OAAO,qBAAqB;AAAA,EAC1C;AAAA,EAEA,IAAI,UAAU;AACZ,WAAO,KAAK,OAAO,WAAW;AAAA,EAChC;AAAA,EAEA,IAAI,QAAQ;AACV,WAAO,KAAK,OAAO,SAAS;AAAA,EAC9B;AAAA,EAEA,aAAqB,eAAe,MAAkB;AACpD,UAAM,cAAc;AACpB,QAAI,cAAc,YAAY,YAAY;AACxC,UAAI;AACF,eAAO,MAAM,OAAO,IAAI;AAAA,MAC1B,SAAS,IAAI;AACX,sBAAc,YAAY,YAAY;AAAA,MACxC;AAAA,IACF;AAEA,WAAO,MAAM,QAAQ,EAAE,OAAO,IAAI,EAAE,OAAO,EAAE,SAAS;AAAA,EACxD;AAAA,EAEA,OAAe,kBAAkB,KAAiC;AAChE,QAAI,IAAI,WAAW,YAAY,GAAG;AAChC,YAAM,OAAO,IAAI,MAAM,GAAG,EAAE,CAAC;AAC7B,UAAI,MAAM;AACR,eAAO,OAAO,KAAK,WAAW,KAAK,KAAK,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;AAAA,MACxE,OAAO;AACL,cAAM,QAAoC;AAAA,UACxC,SAAS;AAAA,UACT,MAAM;AAAA,UACN;AAAA,QACF;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,aAAa,YAAoB;AAC/B,UAAM,MAAM,IAAI,IAAI,UAAU;AAC9B,QAAI,WAAW,IAAI;AACnB,QAAI,OAAO,IAAI;AACf,QAAI,OAAO,IAAI;AACf,UAAM,QAAQ,IAAI;AAClB,QAAI,aAAa,SAAS;AACxB,iBAAW;AACX,aAAO,KAAK;AACZ,aAAO,IAAI,SAAS,SAAS,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,GAAG,IAAI;AACpE,YAAM,OAAO,GAAG,QAAQ,KAAK,IAAI,IAAI,IAAI;AACzC,cAAO,+BAAO,UAAS,IAAI,GAAG,IAAI,IAAI,KAAK,KAAK;AAAA,IAClD,OAAO;AACL,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAyB,eAAe,WAAyB,CAAC,GAA8B;AAE9F,QAAI,CAAC,OAAO,KAAK,QAAQ,GAAG;AAC1B,YAAM,MAAM,0CAA0C;AAAA,IACxD;AACA,UAAM,cAAc,SAAS,OAAO,CAAC,YAAY,QAAQ,WAAW,SAAS;AAC7E,WAAO,MAAM,KAAK,WAAW;AAAA,MAAa,YACxC;AAAA,QACE,MAAM,QAAQ;AAAA,UACZ,YAAY,IAA6B,OAAO,EAAE,IAAI,MAAM;AAC1D,gBAAI;AAGJ,kBAAM,aAAa,uBAAsB,kBAAkB,GAAG;AAE9D,gBAAI,YAAY;AACd,uBAAS;AAAA,gBACP,QAAQC;AAAA,gBACR,YAAY,MAAM,uBAAsB,eAAe,UAAU;AAAA,gBACjE,WAAW;AAAA,gBACX;AAAA,cACF;AAAA,YACF,OAAO;AAEL,oBAAM,aAAa,KAAK,aAAa,GAAG;AACxC,uBAAS,MAAM,KAAK,SAAS,YAAY,GAAG;AAAA,YAC9C;AACA,mBAAO;AAAA,UACT,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,uBAAuB,cAAsB,UAAmC;AAC5F,UAAM,QAAQ,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC3D,SAAG,YAAY,EACZ,QAAQ,KAAK,OAAO,EACpB,OAAO,KAAK,OAAO,KAAK,MAAM,EAC9B,QAAQ,EACR,SAAS,YAAY,KAAK,UAAU,CAAC,OAAO,WAAW;AACtD,YAAI,OAAO;AACT,iBAAO,KAAK;AAAA,QACd,OAAO;AACL,kBAAQ,MAAM;AAAA,QAChB;AAAA,MACF,CAAC;AAAA,IACL,CAAC;AACD,WAAO,yBAAyB,MAAM,SAAS,QAAQ,CAAC;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,yBAAyB,aAAqB;AAC1D,UAAM,cAAc,MAAM,2BAA2B,WAAW;AAChE,WAAO,KAAK,uBAAuB,WAAW;AAAA,EAChD;AAAA;AAAA,EAGA,MAAc,SAAS,KAAa,WAA6C;AArLnF;AAsLI,QAAI;AACJ,QAAI;AACJ,QAAI;AACF,YAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,kBAAY,MAAM,YAAY,QAAQ,OAAO,IAAI;AAAA,IAEnD,SAAS,IAAI;AACX,YAAM,QAAQ;AACd,YAAMC,UAAyB;AAAA,QAC7B,MAAM;AAAA,UACJ,UAAU,MAAM;AAAA,QAClB;AAAA,QACA,QAAQD;AAAA,QACR,WAAW,aAAa;AAAA,MAC1B;AACA,aAAOC;AAAA,IACT;AACA,QAAI;AACF,iBAAW,MAAM,MAAM,IAAI,KAAK;AAAA,QAC9B,cAAc;AAAA,MAChB,CAAC;AAAA,IACH,SAAS,IAAI;AACX,YAAM,aAAa;AACnB,UAAI,WAAW,cAAc;AAE3B,cAAMA,UAAyB;AAAA,UAC7B,MAAM;AAAA,YACJ,WAAW,UAAU,CAAC;AAAA,YACtB,SAAQ,8CAAY,aAAZ,mBAAsB;AAAA,UAChC;AAAA,UACA,QAAQD;AAAA,UACR,WAAW,aAAa;AAAA,QAC1B;AACA,eAAOC;AAAA,MACT,OAAO;AACL,cAAM;AAAA,MACR;AAAA,IACF;AAEA,UAAM,SAAyB;AAAA,MAC7B,MAAM;AAAA,QACJ,QAAQ,SAAS;AAAA,MACnB;AAAA,MACA,QAAQD;AAAA,MACR,WAAW,aAAa;AAAA,IAC1B;AAEA,QAAI,SAAS,UAAU,OAAO,SAAS,SAAS,KAAK;AACnD,YAAM,eAAkC,cAAS,QAAQ,cAAc,MAA/B,mBAAkC;AAC1E,YAAM,CAAC,WAAW,QAAQ,KAAI,2CAAa,MAAM,SAAQ,CAAC,IAAI,EAAE;AAChE,aAAO,OAAO,OAAO,QAAQ,CAAC;AAC9B,aAAO,KAAK,WAAW;AACvB,YAAM,eAAe,OAAO,KAAK,SAAS,MAAM,QAAQ;AAExD,UAAI;AACF,eAAO,KAAK,WAAW,MAAM,SAAS,WAAW,YAAY;AAAA,MAC/D,SAAS,IAAI;AACX,cAAM,QAAQ;AACd,mBAAK,WAAL,mBAAa,MAAM,mBAAmB,MAAM,OAAO;AAAA,MACrD;AAEA,YAAM,eAAe,OAAOE,cAAsC;AAChE,eAAO,aAAa,MAAM,uBAAsB,eAAe,YAAY;AAC3E,eAAO,MAAM,MAAM,KAAK,uBAAuB,cAAcA,SAAQ;AAAA,MACvE;AAEA,YAAM,eAAe,YAAY;AAG/B,YAAI,OAAO,KAAK,QAAQ,GAAG;AACzB,iBAAO,aAAa,MAAM,uBAAsB,eAAe,YAAY;AAC3E,iBAAO,MAAM,MAAM,KAAK,yBAAyB,YAAY;AAAA,QAC/D,OAAO;AACL,iBAAO,OAAO,OAAO,QAAQ,CAAC;AAC9B,iBAAO,KAAK,UAAU;AAAA,QACxB;AAAA,MACF;AAEA,UAAI,WAAmC;AAEvC,cAAQ,SAAS,YAAY,GAAG;AAAA,QAC9B,KAAK;AACH,qBAAW;AACX;AAAA,QACF,KAAK;AAAA,QACL,KAAK;AACH,qBAAW;AACX;AAAA,MACJ;AAEA,cAAQ,WAAW;AAAA,QACjB,KAAK,SAAS;AACZ,gBAAM,aAAa,QAAQ;AAC3B,iBAAO,KAAK,OAAO;AACnB;AAAA,QACF;AAAA,QACA,KAAK,SAAS;AACZ,gBAAM,aAAa;AACnB,iBAAO,KAAK,OAAO;AACnB;AAAA,QACF;AAAA,QACA,SAAS;AACP,gBAAM,CAAC,iBAAiB,MAAI,kBAAO,KAAK,aAAZ,mBAAsB,SAAtB,mBAA4B,MAAM,SAAQ,CAAC,IAAI,EAAE;AAC7E,kBAAQ,mBAAmB;AAAA,YACzB,KAAK,SAAS;AACZ,oBAAM,aAAa;AACnB,qBAAO,KAAK,QAAO,YAAO,KAAK,aAAZ,mBAAsB;AACzC;AAAA,YACF;AAAA,YACA,KAAK,SAAS;AACZ,oBAAM,aAAa;AACnB,qBAAO,KAAK,QAAO,YAAO,KAAK,aAAZ,mBAAsB;AACzC;AAAA,YACF;AAAA,YACA,SAAS;AACP,qBAAO,KAAK,UAAU;AACtB;AAAA,YACF;AAAA,UACF;AACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;;;AF5SO,IAAM,uBAAuB,MAClC;AAAA,EACE,EAAE,UAAU,EAAE,CAACC,qBAAoB,GAAG,EAAE,GAAG,QAAQ,iBAAiB;AAAA,EACpE;AAAA,IACE,SAAS,OAAO,WAAW;AACzB,YAAM,SAAS,MAAM,sBAAsB,OAAO,MAAM;AACxD,aAAO;AAAA,IACT;AAAA,IACA,SAAS,OAAO,WAAW;AACzB,YAAM,SAAS,MAAM,sBAAsB,OAAO,MAAM;AACxD,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AIjBF,cAAc;AAKd,IAAO,cAAQ;","names":["ImageThumbnailSchema","ImageThumbnailSchema","ImageThumbnailSchema","result","encoding","ImageThumbnailSchema"]}
|
|
1
|
+
{"version":3,"sources":["../../src/Plugin.ts","../../src/Witness/Config.ts","../../src/Witness/Witness.ts","../../src/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.ts","../../src/index.ts"],"sourcesContent":["import { ImageThumbnailDiviner } from '@xyo-network/diviner-image-thumbnail'\nimport { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'\nimport { PayloadSetSchema } from '@xyo-network/payload-model'\nimport { createPayloadSetDualPlugin } from '@xyo-network/payloadset-plugin'\n\nimport { ImageThumbnailWitness } from './Witness'\n\nexport const ImageThumbnailPlugin = () =>\n createPayloadSetDualPlugin<ImageThumbnailWitness, ImageThumbnailDiviner>(\n { required: { [ImageThumbnailSchema]: 1 }, schema: PayloadSetSchema },\n {\n diviner: async (params) => {\n const result = await ImageThumbnailDiviner.create(params)\n return result\n },\n witness: async (params) => {\n const result = await ImageThumbnailWitness.create(params)\n return result\n },\n },\n )\n","import { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'\nimport { WitnessConfig } from '@xyo-network/witness-model'\n\nexport const ImageThumbnailWitnessConfigSchema = `${ImageThumbnailSchema}.witness.config` as const\nexport type ImageThumbnailWitnessConfigSchema = typeof ImageThumbnailWitnessConfigSchema\n\nexport type ImageThumbnailEncoding = 'PNG' | 'JPG' | 'GIF'\n\nexport type ImageThumbnailWitnessConfig = WitnessConfig<{\n encoding?: ImageThumbnailEncoding\n height?: number\n ipfsGateway?: string\n maxAsyncProcesses?: number\n maxCacheBytes?: number\n maxCacheEntries?: number\n quality?: number\n schema: ImageThumbnailWitnessConfigSchema\n width?: number\n}>\n","/* eslint-disable max-statements */\nimport { promises as dnsPromises } from 'node:dns'\n\nimport { compact } from '@xylabs/lodash'\nimport { URL } from '@xylabs/url'\nimport { AbstractWitness } from '@xyo-network/abstract-witness'\nimport { axios, AxiosError, AxiosResponse } from '@xyo-network/axios'\nimport { PayloadHasher } from '@xyo-network/core'\nimport { ImageThumbnail, ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'\nimport { UrlPayload, UrlSchema } from '@xyo-network/url-payload-plugin'\nimport { Semaphore } from 'async-mutex'\nimport FileType from 'file-type'\nimport graphicsMagick from 'gm'\nimport hasbin from 'hasbin'\nimport { sha256 } from 'hash-wasm'\nimport shajs from 'sha.js'\nimport Url from 'url-parse'\n\nimport { ImageThumbnailEncoding, ImageThumbnailWitnessConfigSchema } from './Config'\nimport { getVideoFrameAsImageFluent } from './ffmpeg'\nimport { ImageThumbnailWitnessParams } from './Params'\n\n//TODO: Break this into two Witnesses?\n\n// setFfmpegPath(ffmpegPath)\n\n// eslint-disable-next-line import/no-named-as-default-member\nconst gm = graphicsMagick.subClass({ imageMagick: '7+' })\n\nexport interface ImageThumbnailWitnessError extends Error {\n name: 'ImageThumbnailWitnessError'\n url: string\n}\n\nexport interface DnsError extends Error {\n code: string\n}\n\nexport class ImageThumbnailWitness<TParams extends ImageThumbnailWitnessParams = ImageThumbnailWitnessParams> extends AbstractWitness<TParams> {\n static override configSchemas = [ImageThumbnailWitnessConfigSchema]\n\n private _semaphore = new Semaphore(this.maxAsyncProcesses)\n\n get encoding() {\n return this.config.encoding ?? 'PNG'\n }\n\n get height() {\n return this.config.height ?? 128\n }\n\n get ipfGateway() {\n return this.config.ipfsGateway ?? '5d7b6582.beta.decentralnetworkservices.com'\n }\n\n get maxAsyncProcesses() {\n return this.config.maxAsyncProcesses ?? 2\n }\n\n get quality() {\n return this.config.quality ?? 50\n }\n\n get width() {\n return this.config.width ?? 128\n }\n\n private static async binaryToSha256(data: Uint8Array) {\n await PayloadHasher.wasmInitialized\n if (PayloadHasher.wasmSupport.canUseWasm) {\n try {\n return await sha256(data)\n } catch (ex) {\n PayloadHasher.wasmSupport.allowWasm = false\n }\n }\n\n return shajs('sha256').update(data).digest().toString()\n }\n\n private static bufferFromDataUrl(url: string): Buffer | undefined {\n if (url.startsWith('data:image')) {\n const data = url.split(',')[1]\n if (data) {\n return Buffer.from(Uint8Array.from(atob(data), (c) => c.charCodeAt(0)))\n } else {\n const error: ImageThumbnailWitnessError = {\n message: 'Invalid data Url',\n name: 'ImageThumbnailWitnessError',\n url,\n }\n throw error\n }\n }\n }\n\n /**\n * Returns the equivalent IPFS gateway URL for the supplied URL.\n * @param urlToCheck The URL to check\n * @returns If the supplied URL is an IPFS URL, it converts the URL to the\n * equivalent IPFS gateway URL. Otherwise, returns the original URL.\n */\n checkIpfsUrl(urlToCheck: string) {\n const url = new URL(urlToCheck)\n let protocol = url.protocol\n let host = url.host\n let path = url.pathname\n const query = url.search\n if (protocol === 'ipfs:') {\n protocol = 'https:'\n host = this.ipfGateway\n path = url.host === 'ipfs' ? `ipfs${path}` : `ipfs/${url.host}${path}`\n const root = `${protocol}//${host}/${path}`\n return query?.length > 0 ? `${root}?${query}` : root\n } else {\n return urlToCheck\n }\n }\n\n protected override async observeHandler(payloads: UrlPayload[] = []): Promise<ImageThumbnail[]> {\n // eslint-disable-next-line import/no-named-as-default-member\n if (!hasbin.sync('magick')) {\n throw Error('ImageMagick is required for this witness')\n }\n const urlPayloads = payloads.filter((payload) => payload.schema === UrlSchema)\n return await this._semaphore.runExclusive(async () =>\n compact(\n await Promise.all(\n urlPayloads.map<Promise<ImageThumbnail>>(async ({ url }) => {\n let result: ImageThumbnail\n\n //if it is a data URL, return a Buffer\n const dataBuffer = ImageThumbnailWitness.bufferFromDataUrl(url)\n\n if (dataBuffer) {\n result = {\n schema: ImageThumbnailSchema,\n sourceHash: await ImageThumbnailWitness.binaryToSha256(dataBuffer),\n sourceUrl: url,\n url,\n }\n } else {\n //if it is ipfs, go through cloud flair\n const mutatedUrl = this.checkIpfsUrl(url)\n result = await this.fromHttp(mutatedUrl, url)\n }\n return result\n }),\n ),\n ),\n )\n }\n\n private async createThumbnailDataUrl(sourceBuffer: Buffer, encoding?: ImageThumbnailEncoding) {\n const thumb = await new Promise<Buffer>((resolve, reject) => {\n gm(sourceBuffer)\n .quality(this.quality)\n .resize(this.width, this.height)\n .flatten()\n .toBuffer(encoding ?? this.encoding, (error, buffer) => {\n if (error) {\n reject(error)\n } else {\n resolve(buffer)\n }\n })\n })\n return `data:image/png;base64,${thumb.toString('base64')}`\n }\n\n /**\n * Creates an image thumbnail from a video.\n * @param videoBuffer The input video buffer.\n * @returns An buffer containing an image thumbnail for the video.\n */\n private async createThumbnailFromVideo(videoBuffer: Buffer) {\n const imageBuffer = await getVideoFrameAsImageFluent(videoBuffer)\n return this.createThumbnailDataUrl(imageBuffer)\n }\n\n // eslint-disable-next-line complexity\n private async fromHttp(url: string, sourceUrl?: string): Promise<ImageThumbnail> {\n let response: AxiosResponse\n let dnsResult: string[]\n try {\n const urlObj = new Url(url)\n dnsResult = await dnsPromises.resolve(urlObj.host)\n // console.log(`dnsResult: ${JSON.stringify(dnsResult, null, 2)}`)\n } catch (ex) {\n const error = ex as DnsError\n const result: ImageThumbnail = {\n http: {\n code: error.code,\n },\n schema: ImageThumbnailSchema,\n sourceUrl: sourceUrl ?? url,\n }\n return result\n }\n try {\n response = await axios.get(url, {\n responseType: 'arraybuffer',\n })\n } catch (ex) {\n const axiosError = ex as AxiosError\n if (axiosError.isAxiosError) {\n //selectively pick fields from AxiosError\n const result: ImageThumbnail = {\n http: {\n ipAddress: dnsResult[0],\n },\n schema: ImageThumbnailSchema,\n sourceUrl: sourceUrl ?? url,\n }\n if (axiosError?.response?.status !== undefined) {\n result.http = result.http ?? {}\n result.http.status = axiosError?.response?.status\n }\n if (axiosError?.code !== undefined) {\n result.http = result.http ?? {}\n result.http.code = axiosError?.code\n }\n return result\n } else {\n throw ex\n }\n }\n\n const result: ImageThumbnail = {\n http: {\n status: response.status,\n },\n schema: ImageThumbnailSchema,\n sourceUrl: sourceUrl ?? url,\n }\n\n if (response.status >= 200 && response.status < 300) {\n const contentType: string | undefined = response.headers['content-type']?.toString()\n const [mediaType, fileType] = contentType?.split('/') ?? ['', '']\n result.mime = result.mime ?? {}\n result.mime.returned = mediaType\n const sourceBuffer = Buffer.from(response.data, 'binary')\n\n try {\n result.mime.detected = await FileType.fromBuffer(sourceBuffer)\n } catch (ex) {\n const error = ex as Error\n this.logger?.error(`FileType error: ${error.message}`)\n }\n\n const processImage = async (encoding?: ImageThumbnailEncoding) => {\n result.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer)\n result.url = await this.createThumbnailDataUrl(sourceBuffer, encoding)\n }\n\n const processVideo = async () => {\n // Gracefully handle the case where ffmpeg is not installed.\n // eslint-disable-next-line import/no-named-as-default-member\n if (hasbin.sync('ffmpeg')) {\n result.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer)\n result.url = await this.createThumbnailFromVideo(sourceBuffer)\n } else {\n result.mime = result.mime ?? {}\n result.mime.invalid = true\n }\n }\n\n let encoding: ImageThumbnailEncoding = 'PNG'\n\n switch (fileType.toUpperCase()) {\n case 'GIF':\n encoding = 'GIF'\n break\n case 'JPG':\n case 'JPEG':\n encoding = 'JPG'\n break\n }\n\n switch (mediaType) {\n case 'image': {\n await processImage(encoding)\n result.mime.type = mediaType\n break\n }\n case 'video': {\n await processVideo()\n result.mime.type = mediaType\n break\n }\n default: {\n const [detectedMediaType] = result.mime.detected?.mime?.split('/') ?? ['', '']\n switch (detectedMediaType) {\n case 'image': {\n await processImage()\n result.mime.type = result.mime.detected?.mime\n break\n }\n case 'video': {\n await processVideo()\n result.mime.type = result.mime.detected?.mime\n break\n }\n default: {\n result.mime.invalid = true\n break\n }\n }\n break\n }\n }\n }\n return result\n }\n}\n","import { uuid } from '@xyo-network/core'\nimport ffmpeg from 'fluent-ffmpeg'\nimport { unlink, writeFile } from 'fs/promises'\nimport { tmpdir } from 'os'\nimport { Writable, WritableOptions } from 'stream'\n\n/**\n * A Writable stream that collects output from ffmpeg.\n */\nclass FfmpegOutputStream extends Writable {\n private readonly chunks: Uint8Array[] = []\n\n constructor(options?: WritableOptions) {\n super(options)\n }\n\n override _write(chunk: never, _encoding: BufferEncoding, callback: (error?: Error | null) => void): void {\n this.chunks.push(chunk)\n callback()\n }\n\n /**\n * Collects the output from ffmpeg into a buffer.\n * @returns A buffer containing the concatenated\n * output from ffmpeg.\n */\n toBuffer = () => Buffer.concat(this.chunks)\n}\n\n/**\n * Execute FFmpeg using fluent API with provided input buffer and video thumbnail image.\n * @param videoBuffer Input video buffer.\n * @returns Output buffer containing the video thumbnail image.\n */\nexport const getVideoFrameAsImageFluent = async (videoBuffer: Buffer) => {\n // Get a temp file name\n const tmpFile = `/${tmpdir()}/${uuid()}`\n try {\n // Write videoBuffer to temp file for use as input to ffmpeg to\n // avoid issues with ffmpeg inferring premature EOF from buffer\n // passed via stdin (happens when ffmpeg is trying to infer\n // input video format)\n await writeFile(tmpFile, videoBuffer, { encoding: 'binary' })\n const imageBuffer = await new Promise<Buffer>((resolve, reject) => {\n // Create a Writable stream to collect PNG output from ffmpeg\n const ffmpegOutput = new FfmpegOutputStream()\n // Execute ffmpeg using fluent API\n ffmpeg()\n // NOTE: Uncomment to debug CLI args to ffmpeg\n // .on('start', (commandLine) => console.log('Spawned Ffmpeg with command: ' + commandLine))\n .on('error', (err) => reject(err.message))\n // Listen for the 'end' event to combine the output into a buffer holding the PNG image\n .on('end', () => resolve(ffmpegOutput.toBuffer()))\n .input(tmpFile) // Use temp file as input\n .takeFrames(1) // Only take 1st video frame\n .withNoAudio() // Don't include audio\n .outputOptions('-f image2pipe') // Write output to stdout\n .videoCodec('png') // Force PNG output\n // Start processing and direct ffmpeg stdout to writable stream\n .pipe(ffmpegOutput)\n })\n return imageBuffer\n } finally {\n // Cleanup temp file\n try {\n await unlink(tmpFile)\n } catch {\n // No error here since file doesn't exist\n }\n }\n}\n","import { ImageThumbnailPlugin } from './Plugin'\n\nexport * from './Witness'\nexport * from '@xyo-network/diviner-image-thumbnail'\n\nexport { ImageThumbnailPlugin }\n\n// eslint-disable-next-line import/no-default-export\nexport default ImageThumbnailPlugin\n"],"mappings":"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","names":["ImageThumbnailSchema","ImageThumbnailSchema","ImageThumbnailSchema","result","encoding","ImageThumbnailSchema"]}
|
package/package.json
CHANGED
|
@@ -12,16 +12,16 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@xylabs/lodash": "^2.12.30",
|
|
14
14
|
"@xylabs/url": "^2.12.30",
|
|
15
|
-
"@xyo-network/abstract-witness": "~2.77.
|
|
16
|
-
"@xyo-network/axios": "~2.77.
|
|
17
|
-
"@xyo-network/core": "~2.77.
|
|
18
|
-
"@xyo-network/diviner-image-thumbnail": "~2.77.
|
|
19
|
-
"@xyo-network/image-thumbnail-payload-plugin": "~2.77.
|
|
20
|
-
"@xyo-network/module-model": "~2.77.
|
|
21
|
-
"@xyo-network/payload-model": "~2.77.
|
|
22
|
-
"@xyo-network/payloadset-plugin": "~2.77.
|
|
23
|
-
"@xyo-network/url-payload-plugin": "~2.77.
|
|
24
|
-
"@xyo-network/witness-model": "~2.77.
|
|
15
|
+
"@xyo-network/abstract-witness": "~2.77.4",
|
|
16
|
+
"@xyo-network/axios": "~2.77.4",
|
|
17
|
+
"@xyo-network/core": "~2.77.4",
|
|
18
|
+
"@xyo-network/diviner-image-thumbnail": "~2.77.4",
|
|
19
|
+
"@xyo-network/image-thumbnail-payload-plugin": "~2.77.4",
|
|
20
|
+
"@xyo-network/module-model": "~2.77.4",
|
|
21
|
+
"@xyo-network/payload-model": "~2.77.4",
|
|
22
|
+
"@xyo-network/payloadset-plugin": "~2.77.4",
|
|
23
|
+
"@xyo-network/url-payload-plugin": "~2.77.4",
|
|
24
|
+
"@xyo-network/witness-model": "~2.77.4",
|
|
25
25
|
"async-mutex": "^0.4.0",
|
|
26
26
|
"file-type": "^16.5.4",
|
|
27
27
|
"fluent-ffmpeg": "^2.1.2",
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
"@types/hasbin": "^1.2.0",
|
|
38
38
|
"@xylabs/ts-scripts-yarn3": "^3.1.12",
|
|
39
39
|
"@xylabs/tsconfig": "^3.1.12",
|
|
40
|
-
"@xyo-network/account": "~2.77.
|
|
41
|
-
"@xyo-network/memory-archivist": "~2.77.
|
|
42
|
-
"@xyo-network/node-memory": "~2.77.
|
|
43
|
-
"@xyo-network/payload-builder": "~2.77.
|
|
44
|
-
"@xyo-network/sentinel": "~2.77.
|
|
45
|
-
"@xyo-network/witness-timestamp": "~2.77.
|
|
40
|
+
"@xyo-network/account": "~2.77.4",
|
|
41
|
+
"@xyo-network/memory-archivist": "~2.77.4",
|
|
42
|
+
"@xyo-network/node-memory": "~2.77.4",
|
|
43
|
+
"@xyo-network/payload-builder": "~2.77.4",
|
|
44
|
+
"@xyo-network/sentinel": "~2.77.4",
|
|
45
|
+
"@xyo-network/witness-timestamp": "~2.77.4",
|
|
46
46
|
"jest": "^29.7.0",
|
|
47
47
|
"jest-mock-extended": "^3.0.5",
|
|
48
48
|
"typescript": "^5.2.2"
|
|
@@ -77,5 +77,5 @@
|
|
|
77
77
|
"url": "https://github.com/XYOracleNetwork/sdk-xyo-client-js.git"
|
|
78
78
|
},
|
|
79
79
|
"sideEffects": false,
|
|
80
|
-
"version": "2.77.
|
|
80
|
+
"version": "2.77.4"
|
|
81
81
|
}
|
package/src/Witness/Witness.ts
CHANGED
|
@@ -190,7 +190,7 @@ export class ImageThumbnailWitness<TParams extends ImageThumbnailWitnessParams =
|
|
|
190
190
|
const error = ex as DnsError
|
|
191
191
|
const result: ImageThumbnail = {
|
|
192
192
|
http: {
|
|
193
|
-
|
|
193
|
+
code: error.code,
|
|
194
194
|
},
|
|
195
195
|
schema: ImageThumbnailSchema,
|
|
196
196
|
sourceUrl: sourceUrl ?? url,
|
|
@@ -208,11 +208,18 @@ export class ImageThumbnailWitness<TParams extends ImageThumbnailWitnessParams =
|
|
|
208
208
|
const result: ImageThumbnail = {
|
|
209
209
|
http: {
|
|
210
210
|
ipAddress: dnsResult[0],
|
|
211
|
-
status: axiosError?.response?.status,
|
|
212
211
|
},
|
|
213
212
|
schema: ImageThumbnailSchema,
|
|
214
213
|
sourceUrl: sourceUrl ?? url,
|
|
215
214
|
}
|
|
215
|
+
if (axiosError?.response?.status !== undefined) {
|
|
216
|
+
result.http = result.http ?? {}
|
|
217
|
+
result.http.status = axiosError?.response?.status
|
|
218
|
+
}
|
|
219
|
+
if (axiosError?.code !== undefined) {
|
|
220
|
+
result.http = result.http ?? {}
|
|
221
|
+
result.http.code = axiosError?.code
|
|
222
|
+
}
|
|
216
223
|
return result
|
|
217
224
|
} else {
|
|
218
225
|
throw ex
|