@xyo-network/image-thumbnail-plugin 2.75.14 → 2.75.16

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.
Files changed (74) hide show
  1. package/dist/docs.json +4026 -3124
  2. package/dist/node/Plugin.d.cts +4 -4
  3. package/dist/node/Plugin.d.mts +4 -4
  4. package/dist/node/Plugin.d.ts +4 -4
  5. package/dist/node/Witness/Config.d.cts +1 -1
  6. package/dist/node/Witness/Config.d.cts.map +1 -1
  7. package/dist/node/Witness/Config.d.mts +1 -1
  8. package/dist/node/Witness/Config.d.mts.map +1 -1
  9. package/dist/node/Witness/Config.d.ts +1 -1
  10. package/dist/node/Witness/Config.d.ts.map +1 -1
  11. package/dist/node/Witness/Params.d.cts +2 -2
  12. package/dist/node/Witness/Params.d.cts.map +1 -1
  13. package/dist/node/Witness/Params.d.mts +2 -2
  14. package/dist/node/Witness/Params.d.mts.map +1 -1
  15. package/dist/node/Witness/Params.d.ts +2 -2
  16. package/dist/node/Witness/Params.d.ts.map +1 -1
  17. package/dist/node/Witness/Witness.d.cts +1 -1
  18. package/dist/node/Witness/Witness.d.cts.map +1 -1
  19. package/dist/node/Witness/Witness.d.mts +1 -1
  20. package/dist/node/Witness/Witness.d.mts.map +1 -1
  21. package/dist/node/Witness/Witness.d.ts +1 -1
  22. package/dist/node/Witness/Witness.d.ts.map +1 -1
  23. package/dist/node/index.js +2 -2
  24. package/dist/node/index.js.map +1 -1
  25. package/dist/node/index.mjs +1 -1
  26. package/dist/node/index.mjs.map +1 -1
  27. package/package.json +21 -20
  28. package/src/Witness/Config.ts +1 -1
  29. package/src/Witness/Params.ts +2 -2
  30. package/src/Witness/Witness.ts +1 -1
  31. package/dist/node/Plugin.js +0 -357
  32. package/dist/node/Plugin.js.map +0 -1
  33. package/dist/node/Plugin.mjs +0 -322
  34. package/dist/node/Plugin.mjs.map +0 -1
  35. package/dist/node/Witness/Config.js +0 -32
  36. package/dist/node/Witness/Config.js.map +0 -1
  37. package/dist/node/Witness/Config.mjs +0 -7
  38. package/dist/node/Witness/Config.mjs.map +0 -1
  39. package/dist/node/Witness/Params.js +0 -19
  40. package/dist/node/Witness/Params.js.map +0 -1
  41. package/dist/node/Witness/Params.mjs +0 -1
  42. package/dist/node/Witness/Params.mjs.map +0 -1
  43. package/dist/node/Witness/Witness.js +0 -336
  44. package/dist/node/Witness/Witness.js.map +0 -1
  45. package/dist/node/Witness/Witness.mjs +0 -301
  46. package/dist/node/Witness/Witness.mjs.map +0 -1
  47. package/dist/node/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.js +0 -77
  48. package/dist/node/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.js.map +0 -1
  49. package/dist/node/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.mjs +0 -42
  50. package/dist/node/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.mjs.map +0 -1
  51. package/dist/node/Witness/ffmpeg/fluent/index.js +0 -79
  52. package/dist/node/Witness/ffmpeg/fluent/index.js.map +0 -1
  53. package/dist/node/Witness/ffmpeg/fluent/index.mjs +0 -42
  54. package/dist/node/Witness/ffmpeg/fluent/index.mjs.map +0 -1
  55. package/dist/node/Witness/ffmpeg/index.js +0 -109
  56. package/dist/node/Witness/ffmpeg/index.js.map +0 -1
  57. package/dist/node/Witness/ffmpeg/index.mjs +0 -70
  58. package/dist/node/Witness/ffmpeg/index.mjs.map +0 -1
  59. package/dist/node/Witness/ffmpeg/spawn/executeFfmpeg.js +0 -48
  60. package/dist/node/Witness/ffmpeg/spawn/executeFfmpeg.js.map +0 -1
  61. package/dist/node/Witness/ffmpeg/spawn/executeFfmpeg.mjs +0 -23
  62. package/dist/node/Witness/ffmpeg/spawn/executeFfmpeg.mjs.map +0 -1
  63. package/dist/node/Witness/ffmpeg/spawn/getVideoFrameAsImage.js +0 -56
  64. package/dist/node/Witness/ffmpeg/spawn/getVideoFrameAsImage.js.map +0 -1
  65. package/dist/node/Witness/ffmpeg/spawn/getVideoFrameAsImage.mjs +0 -29
  66. package/dist/node/Witness/ffmpeg/spawn/getVideoFrameAsImage.mjs.map +0 -1
  67. package/dist/node/Witness/ffmpeg/spawn/index.js +0 -58
  68. package/dist/node/Witness/ffmpeg/spawn/index.js.map +0 -1
  69. package/dist/node/Witness/ffmpeg/spawn/index.mjs +0 -30
  70. package/dist/node/Witness/ffmpeg/spawn/index.mjs.map +0 -1
  71. package/dist/node/Witness/index.js +0 -340
  72. package/dist/node/Witness/index.js.map +0 -1
  73. package/dist/node/Witness/index.mjs +0 -302
  74. package/dist/node/Witness/index.mjs.map +0 -1
@@ -1,302 +0,0 @@
1
- // src/Witness/Config.ts
2
- import { ImageThumbnailSchema } from "@xyo-network/image-thumbnail-payload-plugin";
3
- var ImageThumbnailWitnessConfigSchema = `${ImageThumbnailSchema}.witness.config`;
4
-
5
- // src/Witness/Witness.ts
6
- import { promises as dnsPromises } from "node:dns";
7
- import { compact } from "@xylabs/lodash";
8
- import { URL } from "@xylabs/url";
9
- import { axios } from "@xyo-network/axios";
10
- import { PayloadHasher } from "@xyo-network/core";
11
- import { ImageThumbnailSchema as ImageThumbnailSchema2 } from "@xyo-network/image-thumbnail-payload-plugin";
12
- import { UrlSchema } from "@xyo-network/url-payload-plugin";
13
- import { AbstractWitness } from "@xyo-network/witness";
14
- import { Semaphore } from "async-mutex";
15
- import FileType from "file-type";
16
- import graphicsMagick from "gm";
17
- import hasbin from "hasbin";
18
- import { sha256 } from "hash-wasm";
19
- import shajs from "sha.js";
20
- import Url from "url-parse";
21
-
22
- // src/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.ts
23
- import { uuid } from "@xyo-network/core";
24
- import ffmpeg from "fluent-ffmpeg";
25
- import { unlink, writeFile } from "fs/promises";
26
- import { tmpdir } from "os";
27
- import { Writable } from "stream";
28
- var FfmpegOutputStream = class extends Writable {
29
- chunks = [];
30
- constructor(options) {
31
- super(options);
32
- }
33
- _write(chunk, _encoding, callback) {
34
- this.chunks.push(chunk);
35
- callback();
36
- }
37
- /**
38
- * Collects the output from ffmpeg into a buffer.
39
- * @returns A buffer containing the concatenated
40
- * output from ffmpeg.
41
- */
42
- toBuffer = () => Buffer.concat(this.chunks);
43
- };
44
- var getVideoFrameAsImageFluent = async (videoBuffer) => {
45
- const tmpFile = `/${tmpdir()}/${uuid()}`;
46
- try {
47
- await writeFile(tmpFile, videoBuffer, { encoding: "binary" });
48
- const imageBuffer = await new Promise((resolve, reject) => {
49
- const ffmpegOutput = new FfmpegOutputStream();
50
- ffmpeg().on("error", (err) => reject(err.message)).on("end", () => resolve(ffmpegOutput.toBuffer())).input(tmpFile).takeFrames(1).withNoAudio().outputOptions("-f image2pipe").videoCodec("png").pipe(ffmpegOutput);
51
- });
52
- return imageBuffer;
53
- } finally {
54
- try {
55
- await unlink(tmpFile);
56
- } catch {
57
- }
58
- }
59
- };
60
-
61
- // src/Witness/Witness.ts
62
- var gm = graphicsMagick.subClass({ imageMagick: "7+" });
63
- var ImageThumbnailWitness = class _ImageThumbnailWitness extends AbstractWitness {
64
- static configSchemas = [ImageThumbnailWitnessConfigSchema];
65
- _semaphore = new Semaphore(this.maxAsyncProcesses);
66
- get encoding() {
67
- return this.config.encoding ?? "PNG";
68
- }
69
- get height() {
70
- return this.config.height ?? 128;
71
- }
72
- get ipfGateway() {
73
- return this.config.ipfsGateway ?? "5d7b6582.beta.decentralnetworkservices.com";
74
- }
75
- get maxAsyncProcesses() {
76
- return this.config.maxAsyncProcesses ?? 2;
77
- }
78
- get quality() {
79
- return this.config.quality ?? 50;
80
- }
81
- get width() {
82
- return this.config.width ?? 128;
83
- }
84
- static async binaryToSha256(data) {
85
- await PayloadHasher.wasmInitialized;
86
- if (PayloadHasher.wasmSupport.canUseWasm) {
87
- try {
88
- return await sha256(data);
89
- } catch (ex) {
90
- PayloadHasher.wasmSupport.allowWasm = false;
91
- }
92
- }
93
- return shajs("sha256").update(data).digest().toString();
94
- }
95
- static bufferFromDataUrl(url) {
96
- if (url.startsWith("data:image")) {
97
- const data = url.split(",")[1];
98
- if (data) {
99
- return Buffer.from(Uint8Array.from(atob(data), (c) => c.charCodeAt(0)));
100
- } else {
101
- const error = {
102
- message: "Invalid data Url",
103
- name: "ImageThumbnailWitnessError",
104
- url
105
- };
106
- throw error;
107
- }
108
- }
109
- }
110
- /**
111
- * Returns the equivalent IPFS gateway URL for the supplied URL.
112
- * @param urlToCheck The URL to check
113
- * @returns If the supplied URL is an IPFS URL, it converts the URL to the
114
- * equivalent IPFS gateway URL. Otherwise, returns the original URL.
115
- */
116
- checkIpfsUrl(urlToCheck) {
117
- const url = new URL(urlToCheck);
118
- let protocol = url.protocol;
119
- let host = url.host;
120
- let path = url.pathname;
121
- const query = url.search;
122
- if (protocol === "ipfs:") {
123
- protocol = "https:";
124
- host = this.ipfGateway;
125
- path = url.host === "ipfs" ? `ipfs${path}` : `ipfs/${url.host}${path}`;
126
- const root = `${protocol}//${host}/${path}`;
127
- return (query == null ? void 0 : query.length) > 0 ? `${root}?${query}` : root;
128
- } else {
129
- return urlToCheck;
130
- }
131
- }
132
- async observeHandler(payloads = []) {
133
- if (!hasbin.sync("magick")) {
134
- throw Error("ImageMagick is required for this witness");
135
- }
136
- const urlPayloads = payloads.filter((payload) => payload.schema === UrlSchema);
137
- return await this._semaphore.runExclusive(
138
- async () => compact(
139
- await Promise.all(
140
- urlPayloads.map(async ({ url }) => {
141
- let result;
142
- const dataBuffer = _ImageThumbnailWitness.bufferFromDataUrl(url);
143
- if (dataBuffer) {
144
- result = {
145
- schema: ImageThumbnailSchema2,
146
- sourceHash: await _ImageThumbnailWitness.binaryToSha256(dataBuffer),
147
- sourceUrl: url,
148
- url
149
- };
150
- } else {
151
- const mutatedUrl = this.checkIpfsUrl(url);
152
- result = await this.fromHttp(mutatedUrl, url);
153
- }
154
- return result;
155
- })
156
- )
157
- )
158
- );
159
- }
160
- async createThumbnailDataUrl(sourceBuffer, encoding) {
161
- const thumb = await new Promise((resolve, reject) => {
162
- gm(sourceBuffer).quality(this.quality).resize(this.width, this.height).flatten().toBuffer(encoding ?? this.encoding, (error, buffer) => {
163
- if (error) {
164
- reject(error);
165
- } else {
166
- resolve(buffer);
167
- }
168
- });
169
- });
170
- return `data:image/png;base64,${thumb.toString("base64")}`;
171
- }
172
- /**
173
- * Creates an image thumbnail from a video.
174
- * @param videoBuffer The input video buffer.
175
- * @returns An buffer containing an image thumbnail for the video.
176
- */
177
- async createThumbnailFromVideo(videoBuffer) {
178
- const imageBuffer = await getVideoFrameAsImageFluent(videoBuffer);
179
- return this.createThumbnailDataUrl(imageBuffer);
180
- }
181
- // eslint-disable-next-line complexity
182
- async fromHttp(url, sourceUrl) {
183
- var _a, _b, _c, _d, _e, _f, _g;
184
- let response;
185
- let dnsResult;
186
- try {
187
- const urlObj = new Url(url);
188
- dnsResult = await dnsPromises.resolve(urlObj.host);
189
- } catch (ex) {
190
- const error = ex;
191
- const result2 = {
192
- http: {
193
- dnsError: error.code
194
- },
195
- schema: ImageThumbnailSchema2,
196
- sourceUrl: sourceUrl ?? url
197
- };
198
- return result2;
199
- }
200
- try {
201
- response = await axios.get(url, {
202
- responseType: "arraybuffer"
203
- });
204
- } catch (ex) {
205
- const axiosError = ex;
206
- if (axiosError.isAxiosError) {
207
- const result2 = {
208
- http: {
209
- ipAddress: dnsResult[0],
210
- status: (_a = axiosError == null ? void 0 : axiosError.response) == null ? void 0 : _a.status
211
- },
212
- schema: ImageThumbnailSchema2,
213
- sourceUrl: sourceUrl ?? url
214
- };
215
- return result2;
216
- } else {
217
- throw ex;
218
- }
219
- }
220
- const result = {
221
- http: {
222
- status: response.status
223
- },
224
- schema: ImageThumbnailSchema2,
225
- sourceUrl: sourceUrl ?? url
226
- };
227
- if (response.status >= 200 && response.status < 300) {
228
- const contentType = (_b = response.headers["content-type"]) == null ? void 0 : _b.toString();
229
- const [mediaType, fileType] = (contentType == null ? void 0 : contentType.split("/")) ?? ["", ""];
230
- result.mime = result.mime ?? {};
231
- result.mime.returned = mediaType;
232
- const sourceBuffer = Buffer.from(response.data, "binary");
233
- try {
234
- result.mime.detected = await FileType.fromBuffer(sourceBuffer);
235
- } catch (ex) {
236
- const error = ex;
237
- (_c = this.logger) == null ? void 0 : _c.error(`FileType error: ${error.message}`);
238
- }
239
- const processImage = async (encoding2) => {
240
- result.sourceHash = await _ImageThumbnailWitness.binaryToSha256(sourceBuffer);
241
- result.url = await this.createThumbnailDataUrl(sourceBuffer, encoding2);
242
- };
243
- const processVideo = async () => {
244
- if (hasbin.sync("ffmpeg")) {
245
- result.sourceHash = await _ImageThumbnailWitness.binaryToSha256(sourceBuffer);
246
- result.url = await this.createThumbnailFromVideo(sourceBuffer);
247
- } else {
248
- result.mime = result.mime ?? {};
249
- result.mime.invalid = true;
250
- }
251
- };
252
- let encoding = "PNG";
253
- switch (fileType.toUpperCase()) {
254
- case "GIF":
255
- encoding = "GIF";
256
- break;
257
- case "JPG":
258
- case "JPEG":
259
- encoding = "JPG";
260
- break;
261
- }
262
- switch (mediaType) {
263
- case "image": {
264
- await processImage(encoding);
265
- result.mime.type = mediaType;
266
- break;
267
- }
268
- case "video": {
269
- await processVideo();
270
- result.mime.type = mediaType;
271
- break;
272
- }
273
- default: {
274
- const [detectedMediaType] = ((_e = (_d = result.mime.detected) == null ? void 0 : _d.mime) == null ? void 0 : _e.split("/")) ?? ["", ""];
275
- switch (detectedMediaType) {
276
- case "image": {
277
- await processImage();
278
- result.mime.type = (_f = result.mime.detected) == null ? void 0 : _f.mime;
279
- break;
280
- }
281
- case "video": {
282
- await processVideo();
283
- result.mime.type = (_g = result.mime.detected) == null ? void 0 : _g.mime;
284
- break;
285
- }
286
- default: {
287
- result.mime.invalid = true;
288
- break;
289
- }
290
- }
291
- break;
292
- }
293
- }
294
- }
295
- return result;
296
- }
297
- };
298
- export {
299
- ImageThumbnailWitness,
300
- ImageThumbnailWitnessConfigSchema
301
- };
302
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/Witness/Config.ts","../../../src/Witness/Witness.ts","../../../src/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.ts"],"sourcesContent":["import { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'\nimport { WitnessConfig } from '@xyo-network/witness'\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 { 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 { AbstractWitness } from '@xyo-network/witness'\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":["ImageThumbnailSchema","ImageThumbnailSchema","result","encoding"]}