@xyo-network/image-thumbnail-plugin 2.72.0 → 2.72.2

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 (53) hide show
  1. package/dist/cjs/Witness/Witness.js +70 -9
  2. package/dist/cjs/Witness/Witness.js.map +1 -1
  3. package/dist/cjs/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.js +53 -0
  4. package/dist/cjs/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.js.map +1 -0
  5. package/dist/cjs/Witness/ffmpeg/fluent/index.js +5 -0
  6. package/dist/cjs/Witness/ffmpeg/fluent/index.js.map +1 -0
  7. package/dist/cjs/Witness/ffmpeg/index.js +6 -0
  8. package/dist/cjs/Witness/ffmpeg/index.js.map +1 -0
  9. package/dist/cjs/Witness/ffmpeg/spawn/executeFfmpeg.js +33 -0
  10. package/dist/cjs/Witness/ffmpeg/spawn/executeFfmpeg.js.map +1 -0
  11. package/dist/cjs/Witness/ffmpeg/spawn/getVideoFrameAsImage.js +16 -0
  12. package/dist/cjs/Witness/ffmpeg/spawn/getVideoFrameAsImage.js.map +1 -0
  13. package/dist/cjs/Witness/ffmpeg/spawn/index.js +6 -0
  14. package/dist/cjs/Witness/ffmpeg/spawn/index.js.map +1 -0
  15. package/dist/docs.json +48199 -0
  16. package/dist/esm/Witness/Witness.js +66 -8
  17. package/dist/esm/Witness/Witness.js.map +1 -1
  18. package/dist/esm/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.js +48 -0
  19. package/dist/esm/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.js.map +1 -0
  20. package/dist/esm/Witness/ffmpeg/fluent/index.js +2 -0
  21. package/dist/esm/Witness/ffmpeg/fluent/index.js.map +1 -0
  22. package/dist/esm/Witness/ffmpeg/index.js +3 -0
  23. package/dist/esm/Witness/ffmpeg/index.js.map +1 -0
  24. package/dist/esm/Witness/ffmpeg/spawn/executeFfmpeg.js +29 -0
  25. package/dist/esm/Witness/ffmpeg/spawn/executeFfmpeg.js.map +1 -0
  26. package/dist/esm/Witness/ffmpeg/spawn/getVideoFrameAsImage.js +11 -0
  27. package/dist/esm/Witness/ffmpeg/spawn/getVideoFrameAsImage.js.map +1 -0
  28. package/dist/esm/Witness/ffmpeg/spawn/index.js +3 -0
  29. package/dist/esm/Witness/ffmpeg/spawn/index.js.map +1 -0
  30. package/dist/types/Plugin.d.ts +1 -0
  31. package/dist/types/Plugin.d.ts.map +1 -1
  32. package/dist/types/Witness/Witness.d.ts +11 -0
  33. package/dist/types/Witness/Witness.d.ts.map +1 -1
  34. package/dist/types/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.d.ts +8 -0
  35. package/dist/types/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.d.ts.map +1 -0
  36. package/dist/types/Witness/ffmpeg/fluent/index.d.ts +2 -0
  37. package/dist/types/Witness/ffmpeg/fluent/index.d.ts.map +1 -0
  38. package/dist/types/Witness/ffmpeg/index.d.ts +3 -0
  39. package/dist/types/Witness/ffmpeg/index.d.ts.map +1 -0
  40. package/dist/types/Witness/ffmpeg/spawn/executeFfmpeg.d.ts +9 -0
  41. package/dist/types/Witness/ffmpeg/spawn/executeFfmpeg.d.ts.map +1 -0
  42. package/dist/types/Witness/ffmpeg/spawn/getVideoFrameAsImage.d.ts +8 -0
  43. package/dist/types/Witness/ffmpeg/spawn/getVideoFrameAsImage.d.ts.map +1 -0
  44. package/dist/types/Witness/ffmpeg/spawn/index.d.ts +3 -0
  45. package/dist/types/Witness/ffmpeg/spawn/index.d.ts.map +1 -0
  46. package/package.json +19 -16
  47. package/src/Witness/Witness.ts +70 -8
  48. package/src/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.ts +53 -0
  49. package/src/Witness/ffmpeg/fluent/index.ts +1 -0
  50. package/src/Witness/ffmpeg/index.ts +2 -0
  51. package/src/Witness/ffmpeg/spawn/executeFfmpeg.ts +31 -0
  52. package/src/Witness/ffmpeg/spawn/getVideoFrameAsImage.ts +11 -0
  53. package/src/Witness/ffmpeg/spawn/index.ts +2 -0
@@ -1,3 +1,4 @@
1
+ /* eslint-disable max-statements */
1
2
  import { promises as dnsPromises } from 'node:dns';
2
3
  import { URL } from '@xylabs/url';
3
4
  import { axios } from '@xyo-network/axios';
@@ -5,6 +6,7 @@ import { PayloadHasher } from '@xyo-network/core';
5
6
  import { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin';
6
7
  import { AbstractWitness } from '@xyo-network/witness';
7
8
  import { Semaphore } from 'async-mutex';
9
+ import FileType from 'file-type';
8
10
  import { subClass } from 'gm';
9
11
  import { sync as hasbin } from 'hasbin';
10
12
  import { sha256 } from 'hash-wasm';
@@ -13,7 +15,9 @@ import { LRUCache } from 'lru-cache';
13
15
  import shajs from 'sha.js';
14
16
  import Url from 'url-parse';
15
17
  import { ImageThumbnailWitnessConfigSchema } from './Config';
18
+ import { getVideoFrameAsImageFluent } from './ffmpeg';
16
19
  //TODO: Break this into two Witnesses?
20
+ // setFfmpegPath(ffmpegPath)
17
21
  const gm = subClass({ imageMagick: '7+' });
18
22
  export class ImageThumbnailWitness extends AbstractWitness {
19
23
  static configSchemas = [ImageThumbnailWitnessConfigSchema];
@@ -151,6 +155,15 @@ export class ImageThumbnailWitness extends AbstractWitness {
151
155
  });
152
156
  return `data:image/png;base64,${thumb.toString('base64')}`;
153
157
  }
158
+ /**
159
+ * Creates an image thumbnail from a video.
160
+ * @param videoBuffer The input video buffer.
161
+ * @returns An buffer containing an image thumbnail for the video.
162
+ */
163
+ async createThumbnailFromVideo(videoBuffer) {
164
+ const imageBuffer = await getVideoFrameAsImageFluent(videoBuffer);
165
+ return this.createThumbnailDataUrl(imageBuffer);
166
+ }
154
167
  async fromHttp(url) {
155
168
  let response;
156
169
  let dnsResult;
@@ -202,17 +215,62 @@ export class ImageThumbnailWitness extends AbstractWitness {
202
215
  };
203
216
  if (response.status >= 200 && response.status < 300) {
204
217
  const contentType = response.headers['content-type']?.toString();
205
- if (contentType.split('/')[0] !== 'image') {
206
- // TODO: Use FFMPEG to create a thumbnail for videos
207
- // ffmpeg -i input.mp4 -ss 00:00:05 -vframes 1 output.png
208
- // Then resize thumbnail using
209
- result.mime = result.mime ?? {};
210
- result.mime.invalid = true;
218
+ const mediaType = contentType.split('/')[0];
219
+ result.mime = result.mime ?? {};
220
+ result.mime.returned = mediaType;
221
+ const sourceBuffer = Buffer.from(response.data, 'binary');
222
+ try {
223
+ result.mime.detected = await FileType.fromBuffer(sourceBuffer);
211
224
  }
212
- else {
213
- const sourceBuffer = Buffer.from(response.data, 'binary');
225
+ catch (ex) {
226
+ const error = ex;
227
+ this.logger?.error(`FileType error: ${error.message}`);
228
+ }
229
+ const processImage = async () => {
214
230
  result.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer);
215
231
  result.url = await this.createThumbnailDataUrl(sourceBuffer);
232
+ };
233
+ const processVideo = async () => {
234
+ // Gracefully handle the case where ffmpeg is not installed.
235
+ if (hasbin('ffmpeg')) {
236
+ result.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer);
237
+ result.url = await this.createThumbnailFromVideo(sourceBuffer);
238
+ }
239
+ else {
240
+ result.mime = result.mime ?? {};
241
+ result.mime.invalid = true;
242
+ }
243
+ };
244
+ switch (mediaType) {
245
+ case 'image': {
246
+ await processImage();
247
+ result.mime.type = mediaType;
248
+ break;
249
+ }
250
+ case 'video': {
251
+ await processVideo();
252
+ result.mime.type = mediaType;
253
+ break;
254
+ }
255
+ default: {
256
+ switch (result.mime.detected?.mime) {
257
+ case 'image': {
258
+ await processImage();
259
+ result.mime.type = result.mime.detected?.mime;
260
+ break;
261
+ }
262
+ case 'video': {
263
+ await processVideo();
264
+ result.mime.type = result.mime.detected?.mime;
265
+ break;
266
+ }
267
+ default: {
268
+ result.mime.invalid = true;
269
+ break;
270
+ }
271
+ }
272
+ break;
273
+ }
216
274
  }
217
275
  }
218
276
  return result;
@@ -1 +1 @@
1
- {"version":3,"file":"Witness.js","sourceRoot":"","sources":["../../../src/Witness/Witness.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Witness.js","sourceRoot":"","sources":["../../../src/Witness/Witness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import ffmpeg from 'fluent-ffmpeg';
2
+ import { Readable, Writable } from 'stream';
3
+ /**
4
+ * Execute FFmpeg using fluent API with provided input buffer and video thumbnail image.
5
+ * @param videoBuffer Input video buffer.
6
+ * @returns Output buffer containing the video thumbnail image.
7
+ */
8
+ export const getVideoFrameAsImageFluent = async (videoBuffer) => {
9
+ const imageBuffer = await new Promise((resolve, reject) => {
10
+ // const videoStream = new PassThrough()
11
+ // videoStream.end(videoBuffer)
12
+ const videoStream = new Readable();
13
+ videoStream._read = () => { }; // _read is required but you can noop it
14
+ videoStream.push(videoBuffer);
15
+ videoStream.push(null);
16
+ // Initialize empty array to collect PNG chunks
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ const pngChunks = [];
19
+ // Create a Writable stream to collect PNG output from ffmpeg
20
+ const writableStream = new Writable({
21
+ write(chunk, encoding, callback) {
22
+ pngChunks.push(chunk);
23
+ callback();
24
+ },
25
+ });
26
+ const command = ffmpeg()
27
+ // Uncomment to debug CLI args to ffmpeg
28
+ // .on('start', (commandLine) => console.log('Spawned Ffmpeg with command: ' + commandLine))
29
+ .input(videoStream)
30
+ .takeFrames(1)
31
+ .withNoAudio()
32
+ // Exactly as their docs but does not work
33
+ // .setStartTime('00:00:00')
34
+ // .seekInput(0)
35
+ .on('error', (err) => reject(err.message))
36
+ // Listen for the 'end' event to combine the chunks and create a PNG buffer
37
+ .on('end', () => resolve(Buffer.concat(pngChunks)))
38
+ .on('data', (chunk) => pngChunks.push(chunk))
39
+ // .toFormat('png')
40
+ .outputOptions('-f image2pipe');
41
+ // .outputOptions('-vcodec png')
42
+ // .output(writableStream, { end: true })
43
+ // Start processing
44
+ command.pipe(writableStream);
45
+ });
46
+ return imageBuffer;
47
+ };
48
+ //# sourceMappingURL=getVideoFrameAsImageFluent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getVideoFrameAsImageFluent.js","sourceRoot":"","sources":["../../../../../src/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,eAAe,CAAA;AAClC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAE3C;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,KAAK,EAAE,WAAmB,EAAE,EAAE;IACtE,MAAM,WAAW,GAAG,MAAM,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAChE,wCAAwC;QACxC,+BAA+B;QAE/B,MAAM,WAAW,GAAG,IAAI,QAAQ,EAAE,CAAA;QAClC,WAAW,CAAC,KAAK,GAAG,GAAG,EAAE,GAAE,CAAC,CAAA,CAAC,wCAAwC;QACrE,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QAC7B,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEtB,+CAA+C;QAC/C,8DAA8D;QAC9D,MAAM,SAAS,GAAU,EAAE,CAAA;QAE3B,6DAA6D;QAC7D,MAAM,cAAc,GAAG,IAAI,QAAQ,CAAC;YAClC,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ;gBAC7B,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;gBACrB,QAAQ,EAAE,CAAA;YACZ,CAAC;SACF,CAAC,CAAA;QAEF,MAAM,OAAO,GAAG,MAAM,EAAE;YACtB,wCAAwC;YACxC,4FAA4F;aAC3F,KAAK,CAAC,WAAW,CAAC;aAClB,UAAU,CAAC,CAAC,CAAC;aACb,WAAW,EAAE;YACd,0CAA0C;YAC1C,4BAA4B;YAC5B,gBAAgB;aACf,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC1C,2EAA2E;aAC1E,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;aAClD,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC7C,mBAAmB;aAClB,aAAa,CAAC,eAAe,CAAC,CAAA;QACjC,gCAAgC;QAChC,yCAAyC;QAEzC,mBAAmB;QACnB,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;IAC9B,CAAC,CAAC,CAAA;IACF,OAAO,WAAW,CAAA;AACpB,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export * from './getVideoFrameAsImageFluent';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/Witness/ffmpeg/fluent/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAA"}
@@ -0,0 +1,3 @@
1
+ export * from './fluent';
2
+ export * from './spawn';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/Witness/ffmpeg/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA"}
@@ -0,0 +1,29 @@
1
+ import { spawn } from 'child_process';
2
+ import { PassThrough } from 'stream';
3
+ /**
4
+ * Execute FFmpeg with the provided arguments.
5
+ * @param videoBuffer Input video buffer.
6
+ * @param ffmpegArgs FFmpeg arguments.
7
+ * @returns Output buffer containing the video thumbnail image.
8
+ */
9
+ export const executeFFmpeg = (videoBuffer, ffmpegArgs) => {
10
+ return new Promise((resolve, reject) => {
11
+ const ffmpeg = spawn('ffmpeg', ffmpegArgs);
12
+ // Create a readable stream from the input buffer
13
+ const videoStream = new PassThrough().end(videoBuffer);
14
+ // Pipe the input stream to ffmpeg's stdin
15
+ videoStream.pipe(ffmpeg.stdin);
16
+ const chunks = [];
17
+ ffmpeg.stdout.on('data', (chunk) => chunks.push(chunk));
18
+ // TODO: This is required as we're seeing errors thrown due to
19
+ // how we're piping the data to ffmpeg. Works perfectly though.
20
+ ffmpeg.stdin.on('error', () => { });
21
+ ffmpeg.on('close', (code) => {
22
+ if (code !== 0) {
23
+ return reject(new Error(`FFmpeg exited with code ${code}`));
24
+ }
25
+ resolve(Buffer.concat(chunks));
26
+ });
27
+ });
28
+ };
29
+ //# sourceMappingURL=executeFfmpeg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executeFfmpeg.js","sourceRoot":"","sources":["../../../../../src/Witness/ffmpeg/spawn/executeFfmpeg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAEpC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,WAAmB,EAAE,UAAoB,EAAmB,EAAE;IAC1F,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;QAE1C,iDAAiD;QACjD,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QAEtD,0CAA0C;QAC1C,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAC9B,MAAM,MAAM,GAAa,EAAE,CAAA;QAC3B,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QAC/D,8DAA8D;QAC9D,+DAA+D;QAC/D,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QAClC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YAC1B,IAAI,IAAI,KAAK,CAAC,EAAE;gBACd,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,IAAI,EAAE,CAAC,CAAC,CAAA;aAC5D;YACD,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;QAChC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA"}
@@ -0,0 +1,11 @@
1
+ import { executeFFmpeg } from './executeFfmpeg';
2
+ /**
3
+ * Execute FFmpeg with provided input buffer and return video thumbnail image.
4
+ * @param videoBuffer Input video buffer.
5
+ * @returns Output buffer containing the video thumbnail image.
6
+ */
7
+ export const getVideoFrameAsImage = async (videoBuffer) => {
8
+ const imageBuffer = await executeFFmpeg(videoBuffer, ['-i', 'pipe:', '-ss', '00:00:00', '-vframes', '1', '-f', 'image2pipe', '-']);
9
+ return imageBuffer;
10
+ };
11
+ //# sourceMappingURL=getVideoFrameAsImage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getVideoFrameAsImage.js","sourceRoot":"","sources":["../../../../../src/Witness/ffmpeg/spawn/getVideoFrameAsImage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE/C;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,EAAE,WAAmB,EAAE,EAAE;IAChE,MAAM,WAAW,GAAG,MAAM,aAAa,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,CAAC,CAAC,CAAA;IAClI,OAAO,WAAW,CAAA;AACpB,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ export * from './executeFfmpeg';
2
+ export * from './getVideoFrameAsImage';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/Witness/ffmpeg/spawn/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA"}
@@ -33,6 +33,7 @@ export declare const ImageThumbnailPlugin: () => import("@xyo-network/payloadset
33
33
  readonly sign?: boolean | undefined;
34
34
  readonly storeQueries?: boolean | undefined;
35
35
  } & Omit<{
36
+ archivist?: string | undefined;
36
37
  schema: "network.xyo.image.thumbnail.witness.config";
37
38
  targetSet?: (import("@xyo-network/payload-model").SchemaFields & import("@xyo-network/payload-model").PayloadFields & import("@xyo-network/payload-model").PayloadSet & {
38
39
  schema: "network.xyo.payload.set";
@@ -1 +1 @@
1
- {"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAA;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAA;AAEjD,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAa9B,CAAA"}
1
+ {"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAA;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAA;AAEjD,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAa9B,CAAA"}
@@ -21,7 +21,12 @@ export declare class ImageThumbnailWitness<TParams extends ImageThumbnailWitness
21
21
  status?: number | undefined;
22
22
  } | undefined;
23
23
  mime?: {
24
+ detected?: {
25
+ ext?: string | undefined;
26
+ mime?: string | undefined;
27
+ } | undefined;
24
28
  invalid?: boolean | undefined;
29
+ returned?: string | undefined;
25
30
  type?: string | undefined;
26
31
  } | undefined;
27
32
  sourceHash?: string | undefined;
@@ -48,6 +53,12 @@ export declare class ImageThumbnailWitness<TParams extends ImageThumbnailWitness
48
53
  private static bufferFromDataUrl;
49
54
  protected observeHandler(payloads?: UrlPayload[]): Promise<ImageThumbnail[]>;
50
55
  private createThumbnailDataUrl;
56
+ /**
57
+ * Creates an image thumbnail from a video.
58
+ * @param videoBuffer The input video buffer.
59
+ * @returns An buffer containing an image thumbnail for the video.
60
+ */
61
+ private createThumbnailFromVideo;
51
62
  private fromHttp;
52
63
  }
53
64
  //# sourceMappingURL=Witness.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Witness.d.ts","sourceRoot":"","sources":["../../../src/Witness/Witness.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAwB,MAAM,6CAA6C,CAAA;AAClG,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAMtD,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAKpC,OAAO,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAA;AAMtD,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,MAAM,CAAC,CAAkC;IACjD,OAAO,CAAC,UAAU,CAAwC;IAE1D,IAAI,KAAK;;;;;;;;;;;;;;;gBAUR;IAED,IAAI,QAAQ,UAEX;IAED,IAAI,MAAM,WAET;IAED,IAAI,iBAAiB,WAEpB;IAED,IAAI,aAAa,WAEhB;IAED,IAAI,eAAe,WAElB;IAED,IAAI,OAAO,WAEV;IAED,IAAI,KAAK,WAER;IAED;;;;;OAKG;IACH,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM;mBAiBjB,cAAc;IAanC,OAAO,CAAC,MAAM,CAAC,iBAAiB;cAkBP,cAAc,CAAC,QAAQ,GAAE,UAAU,EAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;YAqCjF,sBAAsB;YAiBtB,QAAQ;CAgEvB"}
1
+ {"version":3,"file":"Witness.d.ts","sourceRoot":"","sources":["../../../src/Witness/Witness.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAwB,MAAM,6CAA6C,CAAA;AAClG,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAOtD,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAMpC,OAAO,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAA;AAQtD,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,MAAM,CAAC,CAAkC;IACjD,OAAO,CAAC,UAAU,CAAwC;IAE1D,IAAI,KAAK;;;;;;;;;;;;;;;;;;;;gBAUR;IAED,IAAI,QAAQ,UAEX;IAED,IAAI,MAAM,WAET;IAED,IAAI,iBAAiB,WAEpB;IAED,IAAI,aAAa,WAEhB;IAED,IAAI,eAAe,WAElB;IAED,IAAI,OAAO,WAEV;IAED,IAAI,KAAK,WAER;IAED;;;;;OAKG;IACH,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM;mBAiBjB,cAAc;IAanC,OAAO,CAAC,MAAM,CAAC,iBAAiB;cAkBP,cAAc,CAAC,QAAQ,GAAE,UAAU,EAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;YAqCjF,sBAAsB;IAiBpC;;;;OAIG;YACW,wBAAwB;YAKxB,QAAQ;CA+GvB"}
@@ -0,0 +1,8 @@
1
+ /// <reference types="node" />
2
+ /**
3
+ * Execute FFmpeg using fluent API with provided input buffer and video thumbnail image.
4
+ * @param videoBuffer Input video buffer.
5
+ * @returns Output buffer containing the video thumbnail image.
6
+ */
7
+ export declare const getVideoFrameAsImageFluent: (videoBuffer: Buffer) => Promise<Buffer>;
8
+ //# sourceMappingURL=getVideoFrameAsImageFluent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getVideoFrameAsImageFluent.d.ts","sourceRoot":"","sources":["../../../../../src/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.ts"],"names":[],"mappings":";AAGA;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,gBAAuB,MAAM,oBA4CnE,CAAA"}
@@ -0,0 +1,2 @@
1
+ export * from './getVideoFrameAsImageFluent';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/Witness/ffmpeg/fluent/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAA"}
@@ -0,0 +1,3 @@
1
+ export * from './fluent';
2
+ export * from './spawn';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/Witness/ffmpeg/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA"}
@@ -0,0 +1,9 @@
1
+ /// <reference types="node" />
2
+ /**
3
+ * Execute FFmpeg with the provided arguments.
4
+ * @param videoBuffer Input video buffer.
5
+ * @param ffmpegArgs FFmpeg arguments.
6
+ * @returns Output buffer containing the video thumbnail image.
7
+ */
8
+ export declare const executeFFmpeg: (videoBuffer: Buffer, ffmpegArgs: string[]) => Promise<Buffer>;
9
+ //# sourceMappingURL=executeFfmpeg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executeFfmpeg.d.ts","sourceRoot":"","sources":["../../../../../src/Witness/ffmpeg/spawn/executeFfmpeg.ts"],"names":[],"mappings":";AAGA;;;;;GAKG;AACH,eAAO,MAAM,aAAa,gBAAiB,MAAM,cAAc,MAAM,EAAE,KAAG,QAAQ,MAAM,CAqBvF,CAAA"}
@@ -0,0 +1,8 @@
1
+ /// <reference types="node" />
2
+ /**
3
+ * Execute FFmpeg with provided input buffer and return video thumbnail image.
4
+ * @param videoBuffer Input video buffer.
5
+ * @returns Output buffer containing the video thumbnail image.
6
+ */
7
+ export declare const getVideoFrameAsImage: (videoBuffer: Buffer) => Promise<Buffer>;
8
+ //# sourceMappingURL=getVideoFrameAsImage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getVideoFrameAsImage.d.ts","sourceRoot":"","sources":["../../../../../src/Witness/ffmpeg/spawn/getVideoFrameAsImage.ts"],"names":[],"mappings":";AAEA;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,gBAAuB,MAAM,oBAG7D,CAAA"}
@@ -0,0 +1,3 @@
1
+ export * from './executeFfmpeg';
2
+ export * from './getVideoFrameAsImage';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/Witness/ffmpeg/spawn/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA"}
package/package.json CHANGED
@@ -13,19 +13,21 @@
13
13
  "@xylabs/assert": "^2.9.3",
14
14
  "@xylabs/delay": "^2.9.3",
15
15
  "@xylabs/url": "^2.9.3",
16
- "@xyo-network/abstract-diviner": "~2.72.0",
17
- "@xyo-network/archivist-model": "~2.72.0",
18
- "@xyo-network/axios": "~2.72.0",
19
- "@xyo-network/core": "~2.72.0",
20
- "@xyo-network/diviner-model": "~2.72.0",
21
- "@xyo-network/diviner-payload-model": "~2.72.0",
22
- "@xyo-network/image-thumbnail-payload-plugin": "~2.72.0",
23
- "@xyo-network/module": "~2.72.0",
24
- "@xyo-network/payload-model": "~2.72.0",
25
- "@xyo-network/payloadset-plugin": "~2.72.0",
26
- "@xyo-network/url-payload-plugin": "~2.72.0",
27
- "@xyo-network/witness": "~2.72.0",
16
+ "@xyo-network/abstract-diviner": "~2.72.2",
17
+ "@xyo-network/archivist-model": "~2.72.2",
18
+ "@xyo-network/axios": "~2.72.2",
19
+ "@xyo-network/core": "~2.72.2",
20
+ "@xyo-network/diviner-model": "~2.72.2",
21
+ "@xyo-network/diviner-payload-model": "~2.72.2",
22
+ "@xyo-network/image-thumbnail-payload-plugin": "~2.72.2",
23
+ "@xyo-network/module": "~2.72.2",
24
+ "@xyo-network/payload-model": "~2.72.2",
25
+ "@xyo-network/payloadset-plugin": "~2.72.2",
26
+ "@xyo-network/url-payload-plugin": "~2.72.2",
27
+ "@xyo-network/witness": "~2.72.2",
28
28
  "async-mutex": "^0.4.0",
29
+ "file-type": "^16.5.4",
30
+ "fluent-ffmpeg": "^2.1.2",
29
31
  "gm": "^1.25.0",
30
32
  "hasbin": "^1.2.3",
31
33
  "hash-wasm": "^4.9.0",
@@ -35,12 +37,13 @@
35
37
  "url-parse": "^1.5.10"
36
38
  },
37
39
  "devDependencies": {
40
+ "@types/fluent-ffmpeg": "^2.1.21",
38
41
  "@types/gm": "^1.25.1",
39
42
  "@types/hasbin": "^1.2.0",
40
43
  "@types/lodash": "^4.14.197",
41
- "@xylabs/ts-scripts-yarn3": "^2.19.1",
42
- "@xylabs/tsconfig": "^2.19.1",
43
- "typescript": "^5.1.6"
44
+ "@xylabs/ts-scripts-yarn3": "^2.19.3",
45
+ "@xylabs/tsconfig": "^2.19.3",
46
+ "typescript": "^5.2.2"
44
47
  },
45
48
  "description": "Primary SDK for using XYO Protocol 2.0",
46
49
  "browser": "dist/esm/index.js",
@@ -75,5 +78,5 @@
75
78
  },
76
79
  "sideEffects": false,
77
80
  "types": "dist/types/index.d.ts",
78
- "version": "2.72.0"
81
+ "version": "2.72.2"
79
82
  }
@@ -1,3 +1,4 @@
1
+ /* eslint-disable max-statements */
1
2
  import { promises as dnsPromises } from 'node:dns'
2
3
 
3
4
  import { URL } from '@xylabs/url'
@@ -7,6 +8,7 @@ import { ImageThumbnail, ImageThumbnailSchema } from '@xyo-network/image-thumbna
7
8
  import { UrlPayload } from '@xyo-network/url-payload-plugin'
8
9
  import { AbstractWitness } from '@xyo-network/witness'
9
10
  import { Semaphore } from 'async-mutex'
11
+ import FileType from 'file-type'
10
12
  import { subClass } from 'gm'
11
13
  import { sync as hasbin } from 'hasbin'
12
14
  import { sha256 } from 'hash-wasm'
@@ -16,10 +18,13 @@ import shajs from 'sha.js'
16
18
  import Url from 'url-parse'
17
19
 
18
20
  import { ImageThumbnailWitnessConfigSchema } from './Config'
21
+ import { getVideoFrameAsImageFluent } from './ffmpeg'
19
22
  import { ImageThumbnailWitnessParams } from './Params'
20
23
 
21
24
  //TODO: Break this into two Witnesses?
22
25
 
26
+ // setFfmpegPath(ffmpegPath)
27
+
23
28
  const gm = subClass({ imageMagick: '7+' })
24
29
 
25
30
  export interface ImageThumbnailWitnessError extends Error {
@@ -185,6 +190,16 @@ export class ImageThumbnailWitness<TParams extends ImageThumbnailWitnessParams =
185
190
  return `data:image/png;base64,${thumb.toString('base64')}`
186
191
  }
187
192
 
193
+ /**
194
+ * Creates an image thumbnail from a video.
195
+ * @param videoBuffer The input video buffer.
196
+ * @returns An buffer containing an image thumbnail for the video.
197
+ */
198
+ private async createThumbnailFromVideo(videoBuffer: Buffer) {
199
+ const imageBuffer = await getVideoFrameAsImageFluent(videoBuffer)
200
+ return this.createThumbnailDataUrl(imageBuffer)
201
+ }
202
+
188
203
  private async fromHttp(url: string): Promise<ImageThumbnail> {
189
204
  let response: AxiosResponse
190
205
  let dnsResult: string[]
@@ -235,17 +250,64 @@ export class ImageThumbnailWitness<TParams extends ImageThumbnailWitnessParams =
235
250
 
236
251
  if (response.status >= 200 && response.status < 300) {
237
252
  const contentType = response.headers['content-type']?.toString()
238
- if (contentType.split('/')[0] !== 'image') {
239
- // TODO: Use FFMPEG to create a thumbnail for videos
240
- // ffmpeg -i input.mp4 -ss 00:00:05 -vframes 1 output.png
241
- // Then resize thumbnail using
242
- result.mime = result.mime ?? {}
243
- result.mime.invalid = true
244
- } else {
245
- const sourceBuffer = Buffer.from(response.data, 'binary')
253
+ const mediaType = contentType.split('/')[0]
254
+ result.mime = result.mime ?? {}
255
+ result.mime.returned = mediaType
256
+ const sourceBuffer = Buffer.from(response.data, 'binary')
257
+ try {
258
+ result.mime.detected = await FileType.fromBuffer(sourceBuffer)
259
+ } catch (ex) {
260
+ const error = ex as Error
261
+ this.logger?.error(`FileType error: ${error.message}`)
262
+ }
263
+
264
+ const processImage = async () => {
246
265
  result.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer)
247
266
  result.url = await this.createThumbnailDataUrl(sourceBuffer)
248
267
  }
268
+
269
+ const processVideo = async () => {
270
+ // Gracefully handle the case where ffmpeg is not installed.
271
+ if (hasbin('ffmpeg')) {
272
+ result.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer)
273
+ result.url = await this.createThumbnailFromVideo(sourceBuffer)
274
+ } else {
275
+ result.mime = result.mime ?? {}
276
+ result.mime.invalid = true
277
+ }
278
+ }
279
+
280
+ switch (mediaType) {
281
+ case 'image': {
282
+ await processImage()
283
+ result.mime.type = mediaType
284
+ break
285
+ }
286
+ case 'video': {
287
+ await processVideo()
288
+ result.mime.type = mediaType
289
+ break
290
+ }
291
+ default: {
292
+ switch (result.mime.detected?.mime) {
293
+ case 'image': {
294
+ await processImage()
295
+ result.mime.type = result.mime.detected?.mime
296
+ break
297
+ }
298
+ case 'video': {
299
+ await processVideo()
300
+ result.mime.type = result.mime.detected?.mime
301
+ break
302
+ }
303
+ default: {
304
+ result.mime.invalid = true
305
+ break
306
+ }
307
+ }
308
+ break
309
+ }
310
+ }
249
311
  }
250
312
  return result
251
313
  }
@@ -0,0 +1,53 @@
1
+ import ffmpeg from 'fluent-ffmpeg'
2
+ import { Readable, Writable } from 'stream'
3
+
4
+ /**
5
+ * Execute FFmpeg using fluent API with provided input buffer and video thumbnail image.
6
+ * @param videoBuffer Input video buffer.
7
+ * @returns Output buffer containing the video thumbnail image.
8
+ */
9
+ export const getVideoFrameAsImageFluent = async (videoBuffer: Buffer) => {
10
+ const imageBuffer = await new Promise<Buffer>((resolve, reject) => {
11
+ // const videoStream = new PassThrough()
12
+ // videoStream.end(videoBuffer)
13
+
14
+ const videoStream = new Readable()
15
+ videoStream._read = () => {} // _read is required but you can noop it
16
+ videoStream.push(videoBuffer)
17
+ videoStream.push(null)
18
+
19
+ // Initialize empty array to collect PNG chunks
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ const pngChunks: any[] = []
22
+
23
+ // Create a Writable stream to collect PNG output from ffmpeg
24
+ const writableStream = new Writable({
25
+ write(chunk, encoding, callback) {
26
+ pngChunks.push(chunk)
27
+ callback()
28
+ },
29
+ })
30
+
31
+ const command = ffmpeg()
32
+ // Uncomment to debug CLI args to ffmpeg
33
+ // .on('start', (commandLine) => console.log('Spawned Ffmpeg with command: ' + commandLine))
34
+ .input(videoStream)
35
+ .takeFrames(1)
36
+ .withNoAudio()
37
+ // Exactly as their docs but does not work
38
+ // .setStartTime('00:00:00')
39
+ // .seekInput(0)
40
+ .on('error', (err) => reject(err.message))
41
+ // Listen for the 'end' event to combine the chunks and create a PNG buffer
42
+ .on('end', () => resolve(Buffer.concat(pngChunks)))
43
+ .on('data', (chunk) => pngChunks.push(chunk))
44
+ // .toFormat('png')
45
+ .outputOptions('-f image2pipe')
46
+ // .outputOptions('-vcodec png')
47
+ // .output(writableStream, { end: true })
48
+
49
+ // Start processing
50
+ command.pipe(writableStream)
51
+ })
52
+ return imageBuffer
53
+ }
@@ -0,0 +1 @@
1
+ export * from './getVideoFrameAsImageFluent'
@@ -0,0 +1,2 @@
1
+ export * from './fluent'
2
+ export * from './spawn'