@xyo-network/image-thumbnail-plugin 5.3.1 → 5.3.3

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.
@@ -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;AAE/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAA;AAEjF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAA;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAYjE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAMzD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAA;AAQ9D,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,gBAAyB,aAAa,EAAE,MAAM,EAAE,CAA8D;IAC9G,gBAAyB,mBAAmB,EAAE,MAAM,CAAoC;IAExF,OAAO,CAAC,UAAU,CAAwC;IAE1D,IAAI,QAAQ,2BAEX;IAED,IAAI,MAAM,WAET;IAED,IAAI,WAAW,WAEd;IAED,IAAI,iBAAiB,WAEpB;IAED,IAAI,OAAO,WAEV;IAED,IAAI,KAAK,WAER;mBAEoB,cAAc;IAcnC,OAAO,CAAC,MAAM,CAAC,iBAAiB;cAgBP,cAAc,CAAC,QAAQ,GAAE,UAAU,EAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;YAsDjF,sBAAsB;IAiBpC;;;;OAIG;YACW,wBAAwB;YAMxB,QAAQ;YAuDR,YAAY;CA6E3B"}
1
+ {"version":3,"file":"Witness.d.ts","sourceRoot":"","sources":["../../../src/Witness/Witness.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAA;AAEjF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAA;AAExD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAYjE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAMzD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAA;AAQ9D,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,gBAAyB,aAAa,EAAE,MAAM,EAAE,CAA8D;IAC9G,gBAAyB,mBAAmB,EAAE,MAAM,CAAoC;IAExF,OAAO,CAAC,UAAU,CAAwC;IAE1D,IAAI,QAAQ,2BAEX;IAED,IAAI,MAAM,WAET;IAED,IAAI,WAAW,WAEd;IAED,IAAI,iBAAiB,WAEpB;IAED,IAAI,OAAO,WAEV;IAED,IAAI,KAAK,WAER;mBAEoB,cAAc;IAcnC,OAAO,CAAC,MAAM,CAAC,iBAAiB;cAgBP,cAAc,CAAC,QAAQ,GAAE,UAAU,EAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;YAsDjF,sBAAsB;IAiBpC;;;;OAIG;YACW,wBAAwB;YAMxB,QAAQ;YAuDR,YAAY;CA6E3B"}
@@ -12,10 +12,10 @@ var ImageThumbnailWitnessConfigSchema = asSchema(`${ImageThumbnailSchema}.witnes
12
12
  // src/Witness/Witness.ts
13
13
  import { Buffer as Buffer2 } from "buffer";
14
14
  import { promises as dnsPromises } from "dns";
15
- import { exists } from "@xylabs/exists";
15
+ import { exists } from "@xylabs/sdk-js";
16
16
  import { AbstractWitness } from "@xyo-network/abstract-witness";
17
- import { ObjectHasher } from "@xyo-network/hash";
18
17
  import { ImageThumbnailSchema as ImageThumbnailSchema2 } from "@xyo-network/image-thumbnail-payload-plugin";
18
+ import { ObjectHasher } from "@xyo-network/sdk-js";
19
19
  import { UrlSchema } from "@xyo-network/url-payload-plugin";
20
20
  import { Semaphore } from "async-mutex";
21
21
  import axios2 from "axios";
@@ -66,7 +66,7 @@ var getVideoFrameAsImageFluent = async (videoBuffer) => {
66
66
  };
67
67
 
68
68
  // src/Witness/lib/checkIpfsUrl.ts
69
- import { assertEx } from "@xylabs/assert";
69
+ import { assertEx } from "@xylabs/sdk-js";
70
70
  var allowIpfsIoRepair = true;
71
71
  var checkIpfsUrl = (urlToCheck, ipfsGateway) => {
72
72
  try {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Plugin.ts","../../src/Witness/Config.ts","../../src/Witness/Witness.ts","../../src/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.ts","../../src/Witness/lib/checkIpfsUrl.ts","../../src/Witness/lib/createDataUrl.ts","../../src/Witness/lib/resolveDynamicSvg.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/index.ts'\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 { asSchema } from '@xyo-network/payload-model'\nimport type { WitnessConfig } from '@xyo-network/witness-model'\n\nexport const ImageThumbnailWitnessConfigSchema = asSchema(`${ImageThumbnailSchema}.witness.config`, true)\nexport type ImageThumbnailWitnessConfigSchema = typeof ImageThumbnailWitnessConfigSchema\n\nexport type ImageThumbnailEncoding = 'PNG' | 'JPG' | 'GIF'\n\nexport type ImageThumbnailWitnessConfig = WitnessConfig<{\n dataUrlPassthrough?: boolean\n encoding?: ImageThumbnailEncoding\n height?: number\n ipfsGateway?: string\n maxAsyncProcesses?: number\n maxCacheBytes?: number\n maxCacheEntries?: number\n quality?: number\n runExclusive?: boolean\n schema: ImageThumbnailWitnessConfigSchema\n width?: number\n}>\n","/* eslint-disable max-statements */\nimport { Buffer } from 'node:buffer'\nimport { promises as dnsPromises } from 'node:dns'\n\nimport { exists } from '@xylabs/exists'\nimport { AbstractWitness } from '@xyo-network/abstract-witness'\nimport { ObjectHasher } from '@xyo-network/hash'\nimport type { ImageThumbnail } from '@xyo-network/image-thumbnail-payload-plugin'\nimport { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'\nimport type { Schema } from '@xyo-network/payload-model'\nimport type { UrlPayload } from '@xyo-network/url-payload-plugin'\nimport { UrlSchema } from '@xyo-network/url-payload-plugin'\nimport { Semaphore } from 'async-mutex'\nimport type { AxiosError, AxiosResponse } from 'axios'\nimport axios from 'axios'\nimport { fileTypeFromBuffer } 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 type { ImageThumbnailEncoding } from './Config.ts'\nimport { ImageThumbnailWitnessConfigSchema } from './Config.ts'\nimport { getVideoFrameAsImageFluent } from './ffmpeg/index.ts'\nimport {\n checkIpfsUrl, createDataUrl, resolveDynamicSvg,\n} from './lib/index.ts'\nimport type { ImageThumbnailWitnessParams } from './Params.ts'\n\n// TODO: Break this into two Witnesses?\n\n// setFfmpegPath(ffmpegPath)\n\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 readonly configSchemas: Schema[] = [...super.configSchemas, ImageThumbnailWitnessConfigSchema]\n static override readonly defaultConfigSchema: Schema = 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 ipfsGateway() {\n return this.config.ipfsGateway ?? '5d7b6582.beta.decentralnetworkservices.com'\n }\n\n get maxAsyncProcesses() {\n return this.config.maxAsyncProcesses ?? 4\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: ArrayBufferLike) {\n const viewData = new Uint8Array(data)\n await ObjectHasher.wasmInitialized\n if (ObjectHasher.wasmSupport.canUseWasm) {\n try {\n return await sha256(viewData)\n } catch {\n ObjectHasher.wasmSupport.allowWasm = false\n }\n }\n\n return shajs('sha256').update(viewData).digest().toString()\n }\n\n private static bufferFromDataUrl(url: string): ArrayBufferLike | undefined {\n if (url.startsWith('data:image')) {\n const data = url.split(',')[1]\n if (data) {\n return Uint8Array.from(atob(data), c => c.codePointAt(0) ?? 0).buffer\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 protected override async observeHandler(payloads: UrlPayload[] = []): Promise<ImageThumbnail[]> {\n if (!hasbin.sync('magick')) {\n throw new Error('ImageMagick is required for this witness')\n }\n const urlPayloads = payloads.filter(payload => payload.schema === UrlSchema)\n const process = async () => {\n return (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 if (this.config.dataUrlPassthrough) {\n result = {\n schema: ImageThumbnailSchema,\n sourceHash: await ImageThumbnailWitness.binaryToSha256(dataBuffer),\n sourceUrl: url,\n url,\n }\n } else {\n let cookedDataBuffer = dataBuffer\n const urlParts = url.split(';')\n const [, contentType] = urlParts[0].split(':')\n if (contentType.startsWith('image/svg')) {\n const [encoding, byteString] = urlParts[1].split(',')\n if (encoding === 'base64') {\n const newSvg = await resolveDynamicSvg(byteString)\n const newSvgDataUrl = createDataUrl(Buffer.from(newSvg).buffer, contentType)\n cookedDataBuffer = ImageThumbnailWitness.bufferFromDataUrl(newSvgDataUrl) ?? dataBuffer\n }\n }\n result = await this.processMedia(\n cookedDataBuffer,\n {\n schema: ImageThumbnailSchema,\n sourceUrl: url,\n },\n contentType,\n )\n }\n } else {\n // if it is ipfs, go through cloud flair\n const mutatedUrl = checkIpfsUrl(url, this.ipfsGateway)\n result = await this.fromHttp(mutatedUrl, url)\n }\n return result\n }),\n )).filter(exists)\n }\n return this.config.runExclusive ? await this._semaphore.runExclusive(() => process()) : process()\n }\n\n private async createThumbnailDataUrl(sourceBuffer: ArrayBufferLike, encoding?: ImageThumbnailEncoding) {\n const thumb = await new Promise<Buffer>((resolve, reject) => {\n gm(Buffer.from(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 createDataUrl(thumb.buffer, 'image/png')\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: ArrayBufferLike) {\n const imageBuffer = await getVideoFrameAsImageFluent(videoBuffer)\n return this.createThumbnailDataUrl(imageBuffer.buffer)\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 } catch (ex) {\n const error = ex as DnsError\n const result: ImageThumbnail = {\n http: { code: error.code },\n schema: ImageThumbnailSchema,\n sourceUrl: sourceUrl ?? url,\n }\n return result\n }\n try {\n response = await axios.get(url, { responseType: 'arraybuffer' })\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: { ipAddress: dnsResult[0] },\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: { status: response.status },\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 sourceBuffer = Buffer.from(response.data, 'binary').buffer\n\n return this.processMedia(sourceBuffer, result, contentType)\n }\n return result\n }\n\n private async processMedia(sourceBuffer: ArrayBufferLike, imageThumbnail: ImageThumbnail, contentType?: string): Promise<ImageThumbnail> {\n const [mediaType, fileType] = contentType?.split('/') ?? ['', '']\n imageThumbnail.mime = imageThumbnail.mime ?? {}\n imageThumbnail.mime.returned = mediaType\n\n try {\n imageThumbnail.mime.detected = await fileTypeFromBuffer(sourceBuffer as ArrayBuffer)\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 imageThumbnail.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer)\n imageThumbnail.url = await this.createThumbnailDataUrl(sourceBuffer, encoding)\n }\n\n const processVideo = async () => {\n // Gracefully handle the case where ffmpeg is not installed.\n\n if (hasbin.sync('ffmpeg')) {\n imageThumbnail.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer)\n imageThumbnail.url = await this.createThumbnailFromVideo(sourceBuffer)\n } else {\n imageThumbnail.mime = imageThumbnail.mime ?? {}\n imageThumbnail.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 }\n case 'JPG':\n case 'JPEG': {\n encoding = 'JPG'\n break\n }\n }\n\n switch (mediaType) {\n case 'image': {\n await processImage(encoding)\n imageThumbnail.mime.type = mediaType\n break\n }\n case 'video': {\n await processVideo()\n imageThumbnail.mime.type = mediaType\n break\n }\n default: {\n const [detectedMediaType] = imageThumbnail.mime.detected?.mime?.split('/') ?? ['', '']\n switch (detectedMediaType) {\n case 'image': {\n await processImage()\n imageThumbnail.mime.type = imageThumbnail.mime.detected?.mime\n break\n }\n case 'video': {\n await processVideo()\n imageThumbnail.mime.type = imageThumbnail.mime.detected?.mime\n break\n }\n default: {\n imageThumbnail.mime.invalid = true\n break\n }\n }\n break\n }\n }\n return imageThumbnail\n }\n}\n","import { unlink, writeFile } from 'node:fs/promises'\nimport { tmpdir } from 'node:os'\nimport type { WritableOptions } from 'node:stream'\nimport { Writable } from 'node:stream'\n\nimport ffmpeg from 'fluent-ffmpeg'\nimport { v4 as uuid } from 'uuid'\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: ArrayBufferLike) => {\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, new Uint8Array(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 // 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 { assertEx } from '@xylabs/assert'\n\nconst allowIpfsIoRepair = true\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 */\nexport const checkIpfsUrl = (urlToCheck: string, ipfsGateway?: string): string => {\n try {\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 = assertEx(ipfsGateway, () => 'No ipfsGateway provided')\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 if (allowIpfsIoRepair && protocol === 'https' && host === 'ipfs.io') {\n protocol = 'https:'\n host = assertEx(ipfsGateway, () => 'No ipfsGateway provided')\n const pathParts = path.split('/')\n if (pathParts[0] === 'ipfs') {\n pathParts.shift()\n }\n path = pathParts.join('/')\n const root = `${protocol}//${host}/${path}`\n return query?.length > 0 ? `${root}?${query}` : root\n } else {\n return urlToCheck\n }\n } catch {\n // const error = ex as Error\n // console.error(`${error.name}:${error.message} [${urlToCheck}]`)\n // console.log(error.stack)\n return urlToCheck\n }\n}\n","import { fromByteArray } from 'base64-js'\n\nexport const createDataUrl = (data: ArrayBufferLike, contextType: string, encoding: 'base64' = 'base64') => {\n return `data:${contextType};${encoding},${fromByteArray(new Uint8Array(data))}`\n}\n","import type { AxiosResponse } from 'axios'\nimport axios from 'axios'\nimport { toByteArray } from 'base64-js'\nimport { Builder, parseStringPromise } from 'xml2js'\n\nexport const resolveDynamicSvg = async (base64Bytes: string) => {\n const decoder = new TextDecoder()\n const bytes = toByteArray(base64Bytes)\n const svg = decoder.decode(bytes)\n const svgObj = await parseStringPromise(svg)\n const svgNode = svgObj['svg']\n const imageResults = (await Promise.all(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n svgNode['image'].map(async (img: any) => [\n img.$,\n await axios.get(img.$.href, { responseType: 'arraybuffer' }),\n ]),\n )) as [string, AxiosResponse][]\n const image = imageResults.map(([href, response]) => {\n if (response.data) {\n const sourceBuffer = Buffer.from(response.data, 'binary')\n return { $: { href: `data:${response.headers['content-type']?.toString()};base64,${sourceBuffer.toString('base64')}` } }\n } else {\n return { $: { href } }\n }\n })\n const updatedSVG = { ...svgObj, svg: { ...svgNode, image } }\n const builder = new Builder()\n return builder.buildObject(updatedSVG)\n}\n","export { ImageThumbnailPlugin as default, ImageThumbnailPlugin } from './Plugin.ts'\nexport * from './Witness/index.ts'\nexport * from '@xyo-network/diviner-image-thumbnail'\n"],"mappings":"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","names":["ImageThumbnailSchema","Buffer","ImageThumbnailSchema","axios","ImageThumbnailSchema","Buffer","result","axios","encoding","ImageThumbnailSchema"]}
1
+ {"version":3,"sources":["../../src/Plugin.ts","../../src/Witness/Config.ts","../../src/Witness/Witness.ts","../../src/Witness/ffmpeg/fluent/getVideoFrameAsImageFluent.ts","../../src/Witness/lib/checkIpfsUrl.ts","../../src/Witness/lib/createDataUrl.ts","../../src/Witness/lib/resolveDynamicSvg.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/index.ts'\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 { asSchema } from '@xyo-network/payload-model'\nimport type { WitnessConfig } from '@xyo-network/witness-model'\n\nexport const ImageThumbnailWitnessConfigSchema = asSchema(`${ImageThumbnailSchema}.witness.config`, true)\nexport type ImageThumbnailWitnessConfigSchema = typeof ImageThumbnailWitnessConfigSchema\n\nexport type ImageThumbnailEncoding = 'PNG' | 'JPG' | 'GIF'\n\nexport type ImageThumbnailWitnessConfig = WitnessConfig<{\n dataUrlPassthrough?: boolean\n encoding?: ImageThumbnailEncoding\n height?: number\n ipfsGateway?: string\n maxAsyncProcesses?: number\n maxCacheBytes?: number\n maxCacheEntries?: number\n quality?: number\n runExclusive?: boolean\n schema: ImageThumbnailWitnessConfigSchema\n width?: number\n}>\n","/* eslint-disable max-statements */\nimport { Buffer } from 'node:buffer'\nimport { promises as dnsPromises } from 'node:dns'\n\nimport { exists } from '@xylabs/sdk-js'\nimport { AbstractWitness } from '@xyo-network/abstract-witness'\nimport type { ImageThumbnail } from '@xyo-network/image-thumbnail-payload-plugin'\nimport { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'\nimport type { Schema } from '@xyo-network/payload-model'\nimport { ObjectHasher } from '@xyo-network/sdk-js'\nimport type { UrlPayload } from '@xyo-network/url-payload-plugin'\nimport { UrlSchema } from '@xyo-network/url-payload-plugin'\nimport { Semaphore } from 'async-mutex'\nimport type { AxiosError, AxiosResponse } from 'axios'\nimport axios from 'axios'\nimport { fileTypeFromBuffer } 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 type { ImageThumbnailEncoding } from './Config.ts'\nimport { ImageThumbnailWitnessConfigSchema } from './Config.ts'\nimport { getVideoFrameAsImageFluent } from './ffmpeg/index.ts'\nimport {\n checkIpfsUrl, createDataUrl, resolveDynamicSvg,\n} from './lib/index.ts'\nimport type { ImageThumbnailWitnessParams } from './Params.ts'\n\n// TODO: Break this into two Witnesses?\n\n// setFfmpegPath(ffmpegPath)\n\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 readonly configSchemas: Schema[] = [...super.configSchemas, ImageThumbnailWitnessConfigSchema]\n static override readonly defaultConfigSchema: Schema = 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 ipfsGateway() {\n return this.config.ipfsGateway ?? '5d7b6582.beta.decentralnetworkservices.com'\n }\n\n get maxAsyncProcesses() {\n return this.config.maxAsyncProcesses ?? 4\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: ArrayBufferLike) {\n const viewData = new Uint8Array(data)\n await ObjectHasher.wasmInitialized\n if (ObjectHasher.wasmSupport.canUseWasm) {\n try {\n return await sha256(viewData)\n } catch {\n ObjectHasher.wasmSupport.allowWasm = false\n }\n }\n\n return shajs('sha256').update(viewData).digest().toString()\n }\n\n private static bufferFromDataUrl(url: string): ArrayBufferLike | undefined {\n if (url.startsWith('data:image')) {\n const data = url.split(',')[1]\n if (data) {\n return Uint8Array.from(atob(data), c => c.codePointAt(0) ?? 0).buffer\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 protected override async observeHandler(payloads: UrlPayload[] = []): Promise<ImageThumbnail[]> {\n if (!hasbin.sync('magick')) {\n throw new Error('ImageMagick is required for this witness')\n }\n const urlPayloads = payloads.filter(payload => payload.schema === UrlSchema)\n const process = async () => {\n return (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 if (this.config.dataUrlPassthrough) {\n result = {\n schema: ImageThumbnailSchema,\n sourceHash: await ImageThumbnailWitness.binaryToSha256(dataBuffer),\n sourceUrl: url,\n url,\n }\n } else {\n let cookedDataBuffer = dataBuffer\n const urlParts = url.split(';')\n const [, contentType] = urlParts[0].split(':')\n if (contentType.startsWith('image/svg')) {\n const [encoding, byteString] = urlParts[1].split(',')\n if (encoding === 'base64') {\n const newSvg = await resolveDynamicSvg(byteString)\n const newSvgDataUrl = createDataUrl(Buffer.from(newSvg).buffer, contentType)\n cookedDataBuffer = ImageThumbnailWitness.bufferFromDataUrl(newSvgDataUrl) ?? dataBuffer\n }\n }\n result = await this.processMedia(\n cookedDataBuffer,\n {\n schema: ImageThumbnailSchema,\n sourceUrl: url,\n },\n contentType,\n )\n }\n } else {\n // if it is ipfs, go through cloud flair\n const mutatedUrl = checkIpfsUrl(url, this.ipfsGateway)\n result = await this.fromHttp(mutatedUrl, url)\n }\n return result\n }),\n )).filter(exists)\n }\n return this.config.runExclusive ? await this._semaphore.runExclusive(() => process()) : process()\n }\n\n private async createThumbnailDataUrl(sourceBuffer: ArrayBufferLike, encoding?: ImageThumbnailEncoding) {\n const thumb = await new Promise<Buffer>((resolve, reject) => {\n gm(Buffer.from(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 createDataUrl(thumb.buffer, 'image/png')\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: ArrayBufferLike) {\n const imageBuffer = await getVideoFrameAsImageFluent(videoBuffer)\n return this.createThumbnailDataUrl(imageBuffer.buffer)\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 } catch (ex) {\n const error = ex as DnsError\n const result: ImageThumbnail = {\n http: { code: error.code },\n schema: ImageThumbnailSchema,\n sourceUrl: sourceUrl ?? url,\n }\n return result\n }\n try {\n response = await axios.get(url, { responseType: 'arraybuffer' })\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: { ipAddress: dnsResult[0] },\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: { status: response.status },\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 sourceBuffer = Buffer.from(response.data, 'binary').buffer\n\n return this.processMedia(sourceBuffer, result, contentType)\n }\n return result\n }\n\n private async processMedia(sourceBuffer: ArrayBufferLike, imageThumbnail: ImageThumbnail, contentType?: string): Promise<ImageThumbnail> {\n const [mediaType, fileType] = contentType?.split('/') ?? ['', '']\n imageThumbnail.mime = imageThumbnail.mime ?? {}\n imageThumbnail.mime.returned = mediaType\n\n try {\n imageThumbnail.mime.detected = await fileTypeFromBuffer(sourceBuffer as ArrayBuffer)\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 imageThumbnail.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer)\n imageThumbnail.url = await this.createThumbnailDataUrl(sourceBuffer, encoding)\n }\n\n const processVideo = async () => {\n // Gracefully handle the case where ffmpeg is not installed.\n\n if (hasbin.sync('ffmpeg')) {\n imageThumbnail.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer)\n imageThumbnail.url = await this.createThumbnailFromVideo(sourceBuffer)\n } else {\n imageThumbnail.mime = imageThumbnail.mime ?? {}\n imageThumbnail.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 }\n case 'JPG':\n case 'JPEG': {\n encoding = 'JPG'\n break\n }\n }\n\n switch (mediaType) {\n case 'image': {\n await processImage(encoding)\n imageThumbnail.mime.type = mediaType\n break\n }\n case 'video': {\n await processVideo()\n imageThumbnail.mime.type = mediaType\n break\n }\n default: {\n const [detectedMediaType] = imageThumbnail.mime.detected?.mime?.split('/') ?? ['', '']\n switch (detectedMediaType) {\n case 'image': {\n await processImage()\n imageThumbnail.mime.type = imageThumbnail.mime.detected?.mime\n break\n }\n case 'video': {\n await processVideo()\n imageThumbnail.mime.type = imageThumbnail.mime.detected?.mime\n break\n }\n default: {\n imageThumbnail.mime.invalid = true\n break\n }\n }\n break\n }\n }\n return imageThumbnail\n }\n}\n","import { unlink, writeFile } from 'node:fs/promises'\nimport { tmpdir } from 'node:os'\nimport type { WritableOptions } from 'node:stream'\nimport { Writable } from 'node:stream'\n\nimport ffmpeg from 'fluent-ffmpeg'\nimport { v4 as uuid } from 'uuid'\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: ArrayBufferLike) => {\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, new Uint8Array(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 // 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 { assertEx } from '@xylabs/sdk-js'\n\nconst allowIpfsIoRepair = true\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 */\nexport const checkIpfsUrl = (urlToCheck: string, ipfsGateway?: string): string => {\n try {\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 = assertEx(ipfsGateway, () => 'No ipfsGateway provided')\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 if (allowIpfsIoRepair && protocol === 'https' && host === 'ipfs.io') {\n protocol = 'https:'\n host = assertEx(ipfsGateway, () => 'No ipfsGateway provided')\n const pathParts = path.split('/')\n if (pathParts[0] === 'ipfs') {\n pathParts.shift()\n }\n path = pathParts.join('/')\n const root = `${protocol}//${host}/${path}`\n return query?.length > 0 ? `${root}?${query}` : root\n } else {\n return urlToCheck\n }\n } catch {\n // const error = ex as Error\n // console.error(`${error.name}:${error.message} [${urlToCheck}]`)\n // console.log(error.stack)\n return urlToCheck\n }\n}\n","import { fromByteArray } from 'base64-js'\n\nexport const createDataUrl = (data: ArrayBufferLike, contextType: string, encoding: 'base64' = 'base64') => {\n return `data:${contextType};${encoding},${fromByteArray(new Uint8Array(data))}`\n}\n","import type { AxiosResponse } from 'axios'\nimport axios from 'axios'\nimport { toByteArray } from 'base64-js'\nimport { Builder, parseStringPromise } from 'xml2js'\n\nexport const resolveDynamicSvg = async (base64Bytes: string) => {\n const decoder = new TextDecoder()\n const bytes = toByteArray(base64Bytes)\n const svg = decoder.decode(bytes)\n const svgObj = await parseStringPromise(svg)\n const svgNode = svgObj['svg']\n const imageResults = (await Promise.all(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n svgNode['image'].map(async (img: any) => [\n img.$,\n await axios.get(img.$.href, { responseType: 'arraybuffer' }),\n ]),\n )) as [string, AxiosResponse][]\n const image = imageResults.map(([href, response]) => {\n if (response.data) {\n const sourceBuffer = Buffer.from(response.data, 'binary')\n return { $: { href: `data:${response.headers['content-type']?.toString()};base64,${sourceBuffer.toString('base64')}` } }\n } else {\n return { $: { href } }\n }\n })\n const updatedSVG = { ...svgObj, svg: { ...svgNode, image } }\n const builder = new Builder()\n return builder.buildObject(updatedSVG)\n}\n","export { ImageThumbnailPlugin as default, ImageThumbnailPlugin } from './Plugin.ts'\nexport * from './Witness/index.ts'\nexport * from '@xyo-network/diviner-image-thumbnail'\n"],"mappings":"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","names":["ImageThumbnailSchema","Buffer","ImageThumbnailSchema","axios","ImageThumbnailSchema","Buffer","result","axios","encoding","ImageThumbnailSchema"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xyo-network/image-thumbnail-plugin",
3
- "version": "5.3.1",
3
+ "version": "5.3.3",
4
4
  "description": "Typescript/Javascript Plugins for XYO Platform",
5
5
  "homepage": "https://xyo.network",
6
6
  "bugs": {
@@ -32,25 +32,53 @@
32
32
  "types": "./dist/node/index.d.ts",
33
33
  "files": [
34
34
  "dist",
35
- "src",
36
- "!**/*.bench.*",
37
- "!**/*.spec.*",
38
- "!**/*.test.*"
35
+ "README.md"
39
36
  ],
40
37
  "dependencies": {
41
- "@xylabs/assert": "~5.0.64",
42
- "@xylabs/exists": "~5.0.64",
43
- "@xyo-network/abstract-witness": "~5.3.2",
44
- "@xyo-network/diviner-image-thumbnail": "5.3.1",
45
- "@xyo-network/hash": "~5.3.2",
46
- "@xyo-network/image-thumbnail-payload-plugin": "5.3.1",
47
- "@xyo-network/module-model": "~5.3.2",
48
- "@xyo-network/payload-model": "~5.3.2",
49
- "@xyo-network/payloadset-plugin": "~5.3.2",
50
- "@xyo-network/url-payload-plugin": "5.3.1",
51
- "@xyo-network/witness-model": "~5.3.2",
38
+ "@xyo-network/diviner-image-thumbnail": "workspace:^",
39
+ "@xyo-network/image-thumbnail-payload-plugin": "workspace:^",
40
+ "@xyo-network/url-payload-plugin": "workspace:^"
41
+ },
42
+ "devDependencies": {
43
+ "@types/fluent-ffmpeg": "~2.1.28",
44
+ "@types/gm": "~1.25.4",
45
+ "@types/hasbin": "~1.2.2",
46
+ "@types/sha.js": "~2.4.4",
47
+ "@types/url-parse": "~1.4.11",
48
+ "@types/uuid": "~11.0.0",
49
+ "@types/xml2js": "~0.4.14",
50
+ "@xylabs/sdk-js": "^5.0.90",
51
+ "@xylabs/ts-scripts-common": "~7.5.10",
52
+ "@xylabs/ts-scripts-yarn3": "~7.5.10",
53
+ "@xylabs/tsconfig": "~7.5.10",
54
+ "@xylabs/vitest-extended": "~5.0.90",
55
+ "@xyo-network/abstract-witness": "~5.3.5",
56
+ "@xyo-network/archivist-memory": "~5.3.5",
57
+ "@xyo-network/archivist-model": "~5.3.5",
58
+ "@xyo-network/archivist-wrapper": "~5.3.5",
59
+ "@xyo-network/boundwitness-builder": "~5.3.5",
60
+ "@xyo-network/boundwitness-model": "~5.3.5",
61
+ "@xyo-network/boundwitness-validator": "~5.3.5",
62
+ "@xyo-network/diviner-abstract": "~5.3.5",
63
+ "@xyo-network/diviner-boundwitness-memory": "~5.3.5",
64
+ "@xyo-network/diviner-indexing": "~5.3.5",
65
+ "@xyo-network/diviner-model": "~5.3.5",
66
+ "@xyo-network/diviner-payload-generic": "~5.3.5",
67
+ "@xyo-network/diviner-payload-model": "~5.3.5",
68
+ "@xyo-network/diviner-wrapper": "~5.3.5",
69
+ "@xyo-network/module-factory-locator": "~5.3.5",
70
+ "@xyo-network/module-model": "~5.3.5",
71
+ "@xyo-network/node-memory": "~5.3.5",
72
+ "@xyo-network/payload-model": "~5.3.5",
73
+ "@xyo-network/payloadset-plugin": "~5.3.5",
74
+ "@xyo-network/sdk-js": "~5.3.5",
75
+ "@xyo-network/sentinel-memory": "~5.3.5",
76
+ "@xyo-network/sentinel-wrapper": "~5.3.5",
77
+ "@xyo-network/wallet": "~5.3.5",
78
+ "@xyo-network/witness-model": "~5.3.5",
79
+ "@xyo-network/witness-timestamp": "~5.3.5",
52
80
  "async-mutex": "~0.5.0",
53
- "axios": "^1.13.4",
81
+ "axios": "^1.13.5",
54
82
  "base64-js": "~1.5.1",
55
83
  "file-type": "~21.3.0",
56
84
  "fluent-ffmpeg": "~2.1.3",
@@ -58,31 +86,34 @@
58
86
  "hasbin": "~1.2.3",
59
87
  "hash-wasm": "~4.12.0",
60
88
  "sha.js": "~2.4.12",
89
+ "typescript": "~5.9.3",
61
90
  "url-parse": "~1.5.10",
62
91
  "uuid": "~13.0.0",
63
- "xml2js": "~0.6.2"
92
+ "vitest": "~4.1.2",
93
+ "xml2js": "~0.6.2",
94
+ "zod": "^4.3.6"
64
95
  },
65
- "devDependencies": {
66
- "@types/fluent-ffmpeg": "~2.1.28",
67
- "@types/gm": "~1.25.4",
68
- "@types/hasbin": "~1.2.2",
69
- "@types/sha.js": "~2.4.4",
70
- "@types/url-parse": "~1.4.11",
71
- "@types/uuid": "~11.0.0",
72
- "@types/xml2js": "~0.4.14",
73
- "@xylabs/delay": "~5.0.64",
74
- "@xylabs/ts-scripts-yarn3": "~7.3.2",
75
- "@xylabs/tsconfig": "~7.3.2",
76
- "@xylabs/vitest-extended": "~5.0.64",
77
- "@xyo-network/account": "~5.3.2",
78
- "@xyo-network/archivist-memory": "~5.3.2",
79
- "@xyo-network/node-memory": "~5.3.2",
80
- "@xyo-network/payload-builder": "~5.3.2",
81
- "@xyo-network/sentinel-memory": "~5.3.2",
82
- "@xyo-network/sentinel-wrapper": "~5.3.2",
83
- "@xyo-network/witness-timestamp": "~5.3.2",
84
- "typescript": "~5.9.3",
85
- "vitest": "~4.0.18"
96
+ "peerDependencies": {
97
+ "@xylabs/sdk-js": "^5",
98
+ "@xyo-network/abstract-witness": "^5",
99
+ "@xyo-network/module-model": "^5",
100
+ "@xyo-network/payload-model": "^5",
101
+ "@xyo-network/payloadset-plugin": "^5",
102
+ "@xyo-network/sdk-js": "^5",
103
+ "@xyo-network/witness-model": "^5",
104
+ "async-mutex": "^0",
105
+ "axios": "^1",
106
+ "base64-js": "^1",
107
+ "file-type": "^21",
108
+ "fluent-ffmpeg": "^2",
109
+ "gm": "^1",
110
+ "hasbin": "^1",
111
+ "hash-wasm": "^4",
112
+ "sha.js": "^2",
113
+ "url-parse": "^1",
114
+ "uuid": "^13",
115
+ "xml2js": "^0",
116
+ "zod": "^4"
86
117
  },
87
118
  "publishConfig": {
88
119
  "access": "public"
package/src/Plugin.ts DELETED
@@ -1,21 +0,0 @@
1
- import { ImageThumbnailDiviner } from '@xyo-network/diviner-image-thumbnail'
2
- import { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'
3
- import { PayloadSetSchema } from '@xyo-network/payload-model'
4
- import { createPayloadSetDualPlugin } from '@xyo-network/payloadset-plugin'
5
-
6
- import { ImageThumbnailWitness } from './Witness/index.ts'
7
-
8
- export const ImageThumbnailPlugin = () =>
9
- createPayloadSetDualPlugin<ImageThumbnailWitness, ImageThumbnailDiviner>(
10
- { required: { [ImageThumbnailSchema]: 1 }, schema: PayloadSetSchema },
11
- {
12
- diviner: async (params) => {
13
- const result = await ImageThumbnailDiviner.create(params)
14
- return result
15
- },
16
- witness: async (params) => {
17
- const result = await ImageThumbnailWitness.create(params)
18
- return result
19
- },
20
- },
21
- )
@@ -1,22 +0,0 @@
1
- import { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'
2
- import { asSchema } from '@xyo-network/payload-model'
3
- import type { WitnessConfig } from '@xyo-network/witness-model'
4
-
5
- export const ImageThumbnailWitnessConfigSchema = asSchema(`${ImageThumbnailSchema}.witness.config`, true)
6
- export type ImageThumbnailWitnessConfigSchema = typeof ImageThumbnailWitnessConfigSchema
7
-
8
- export type ImageThumbnailEncoding = 'PNG' | 'JPG' | 'GIF'
9
-
10
- export type ImageThumbnailWitnessConfig = WitnessConfig<{
11
- dataUrlPassthrough?: boolean
12
- encoding?: ImageThumbnailEncoding
13
- height?: number
14
- ipfsGateway?: string
15
- maxAsyncProcesses?: number
16
- maxCacheBytes?: number
17
- maxCacheEntries?: number
18
- quality?: number
19
- runExclusive?: boolean
20
- schema: ImageThumbnailWitnessConfigSchema
21
- width?: number
22
- }>
@@ -1,10 +0,0 @@
1
- import type { AnyConfigSchema } from '@xyo-network/module-model'
2
- import type { WitnessParams } from '@xyo-network/witness-model'
3
-
4
- import type { ImageThumbnailWitnessConfig } from './Config.ts'
5
-
6
- export interface ImageThumbnailWitnessParams extends WitnessParams<
7
- AnyConfigSchema<ImageThumbnailWitnessConfig>>
8
- {
9
- loaders?: string[]
10
- }
@@ -1,320 +0,0 @@
1
- /* eslint-disable max-statements */
2
- import { Buffer } from 'node:buffer'
3
- import { promises as dnsPromises } from 'node:dns'
4
-
5
- import { exists } from '@xylabs/exists'
6
- import { AbstractWitness } from '@xyo-network/abstract-witness'
7
- import { ObjectHasher } from '@xyo-network/hash'
8
- import type { ImageThumbnail } from '@xyo-network/image-thumbnail-payload-plugin'
9
- import { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'
10
- import type { Schema } from '@xyo-network/payload-model'
11
- import type { UrlPayload } from '@xyo-network/url-payload-plugin'
12
- import { UrlSchema } from '@xyo-network/url-payload-plugin'
13
- import { Semaphore } from 'async-mutex'
14
- import type { AxiosError, AxiosResponse } from 'axios'
15
- import axios from 'axios'
16
- import { fileTypeFromBuffer } from 'file-type'
17
- import graphicsMagick from 'gm'
18
- import hasbin from 'hasbin'
19
- import { sha256 } from 'hash-wasm'
20
- import shajs from 'sha.js'
21
- import Url from 'url-parse'
22
-
23
- import type { ImageThumbnailEncoding } from './Config.ts'
24
- import { ImageThumbnailWitnessConfigSchema } from './Config.ts'
25
- import { getVideoFrameAsImageFluent } from './ffmpeg/index.ts'
26
- import {
27
- checkIpfsUrl, createDataUrl, resolveDynamicSvg,
28
- } from './lib/index.ts'
29
- import type { ImageThumbnailWitnessParams } from './Params.ts'
30
-
31
- // TODO: Break this into two Witnesses?
32
-
33
- // setFfmpegPath(ffmpegPath)
34
-
35
- const gm = graphicsMagick.subClass({ imageMagick: '7+' })
36
-
37
- export interface ImageThumbnailWitnessError extends Error {
38
- name: 'ImageThumbnailWitnessError'
39
- url: string
40
- }
41
-
42
- export interface DnsError extends Error {
43
- code: string
44
- }
45
-
46
- export class ImageThumbnailWitness<TParams extends ImageThumbnailWitnessParams = ImageThumbnailWitnessParams> extends AbstractWitness<TParams> {
47
- static override readonly configSchemas: Schema[] = [...super.configSchemas, ImageThumbnailWitnessConfigSchema]
48
- static override readonly defaultConfigSchema: Schema = ImageThumbnailWitnessConfigSchema
49
-
50
- private _semaphore = new Semaphore(this.maxAsyncProcesses)
51
-
52
- get encoding() {
53
- return this.config.encoding ?? 'PNG'
54
- }
55
-
56
- get height() {
57
- return this.config.height ?? 128
58
- }
59
-
60
- get ipfsGateway() {
61
- return this.config.ipfsGateway ?? '5d7b6582.beta.decentralnetworkservices.com'
62
- }
63
-
64
- get maxAsyncProcesses() {
65
- return this.config.maxAsyncProcesses ?? 4
66
- }
67
-
68
- get quality() {
69
- return this.config.quality ?? 50
70
- }
71
-
72
- get width() {
73
- return this.config.width ?? 128
74
- }
75
-
76
- private static async binaryToSha256(data: ArrayBufferLike) {
77
- const viewData = new Uint8Array(data)
78
- await ObjectHasher.wasmInitialized
79
- if (ObjectHasher.wasmSupport.canUseWasm) {
80
- try {
81
- return await sha256(viewData)
82
- } catch {
83
- ObjectHasher.wasmSupport.allowWasm = false
84
- }
85
- }
86
-
87
- return shajs('sha256').update(viewData).digest().toString()
88
- }
89
-
90
- private static bufferFromDataUrl(url: string): ArrayBufferLike | undefined {
91
- if (url.startsWith('data:image')) {
92
- const data = url.split(',')[1]
93
- if (data) {
94
- return Uint8Array.from(atob(data), c => c.codePointAt(0) ?? 0).buffer
95
- } else {
96
- const error: ImageThumbnailWitnessError = {
97
- message: 'Invalid data Url',
98
- name: 'ImageThumbnailWitnessError',
99
- url,
100
- }
101
- throw error
102
- }
103
- }
104
- }
105
-
106
- protected override async observeHandler(payloads: UrlPayload[] = []): Promise<ImageThumbnail[]> {
107
- if (!hasbin.sync('magick')) {
108
- throw new Error('ImageMagick is required for this witness')
109
- }
110
- const urlPayloads = payloads.filter(payload => payload.schema === UrlSchema)
111
- const process = async () => {
112
- return (await Promise.all(
113
- urlPayloads.map<Promise<ImageThumbnail>>(async ({ url }) => {
114
- let result: ImageThumbnail
115
-
116
- // if it is a data URL, return a Buffer
117
- const dataBuffer = ImageThumbnailWitness.bufferFromDataUrl(url)
118
-
119
- if (dataBuffer) {
120
- if (this.config.dataUrlPassthrough) {
121
- result = {
122
- schema: ImageThumbnailSchema,
123
- sourceHash: await ImageThumbnailWitness.binaryToSha256(dataBuffer),
124
- sourceUrl: url,
125
- url,
126
- }
127
- } else {
128
- let cookedDataBuffer = dataBuffer
129
- const urlParts = url.split(';')
130
- const [, contentType] = urlParts[0].split(':')
131
- if (contentType.startsWith('image/svg')) {
132
- const [encoding, byteString] = urlParts[1].split(',')
133
- if (encoding === 'base64') {
134
- const newSvg = await resolveDynamicSvg(byteString)
135
- const newSvgDataUrl = createDataUrl(Buffer.from(newSvg).buffer, contentType)
136
- cookedDataBuffer = ImageThumbnailWitness.bufferFromDataUrl(newSvgDataUrl) ?? dataBuffer
137
- }
138
- }
139
- result = await this.processMedia(
140
- cookedDataBuffer,
141
- {
142
- schema: ImageThumbnailSchema,
143
- sourceUrl: url,
144
- },
145
- contentType,
146
- )
147
- }
148
- } else {
149
- // if it is ipfs, go through cloud flair
150
- const mutatedUrl = checkIpfsUrl(url, this.ipfsGateway)
151
- result = await this.fromHttp(mutatedUrl, url)
152
- }
153
- return result
154
- }),
155
- )).filter(exists)
156
- }
157
- return this.config.runExclusive ? await this._semaphore.runExclusive(() => process()) : process()
158
- }
159
-
160
- private async createThumbnailDataUrl(sourceBuffer: ArrayBufferLike, encoding?: ImageThumbnailEncoding) {
161
- const thumb = await new Promise<Buffer>((resolve, reject) => {
162
- gm(Buffer.from(sourceBuffer))
163
- .quality(this.quality)
164
- .resize(this.width, this.height)
165
- .flatten()
166
- .toBuffer(encoding ?? this.encoding, (error, buffer) => {
167
- if (error) {
168
- reject(error)
169
- } else {
170
- resolve(buffer)
171
- }
172
- })
173
- })
174
- return createDataUrl(thumb.buffer, 'image/png')
175
- }
176
-
177
- /**
178
- * Creates an image thumbnail from a video.
179
- * @param videoBuffer The input video buffer.
180
- * @returns An buffer containing an image thumbnail for the video.
181
- */
182
- private async createThumbnailFromVideo(videoBuffer: ArrayBufferLike) {
183
- const imageBuffer = await getVideoFrameAsImageFluent(videoBuffer)
184
- return this.createThumbnailDataUrl(imageBuffer.buffer)
185
- }
186
-
187
- // eslint-disable-next-line complexity
188
- private async fromHttp(url: string, sourceUrl?: string): Promise<ImageThumbnail> {
189
- let response: AxiosResponse
190
- let dnsResult: string[]
191
- try {
192
- const urlObj = new Url(url)
193
- dnsResult = await dnsPromises.resolve(urlObj.host)
194
- } catch (ex) {
195
- const error = ex as DnsError
196
- const result: ImageThumbnail = {
197
- http: { code: error.code },
198
- schema: ImageThumbnailSchema,
199
- sourceUrl: sourceUrl ?? url,
200
- }
201
- return result
202
- }
203
- try {
204
- response = await axios.get(url, { responseType: 'arraybuffer' })
205
- } catch (ex) {
206
- const axiosError = ex as AxiosError
207
- if (axiosError.isAxiosError) {
208
- // selectively pick fields from AxiosError
209
- const result: ImageThumbnail = {
210
- http: { ipAddress: dnsResult[0] },
211
- schema: ImageThumbnailSchema,
212
- sourceUrl: sourceUrl ?? url,
213
- }
214
- if (axiosError?.response?.status !== undefined) {
215
- result.http = result.http ?? {}
216
- result.http.status = axiosError?.response?.status
217
- }
218
- if (axiosError?.code !== undefined) {
219
- result.http = result.http ?? {}
220
- result.http.code = axiosError?.code
221
- }
222
- return result
223
- } else {
224
- throw ex
225
- }
226
- }
227
-
228
- const result: ImageThumbnail = {
229
- http: { status: response.status },
230
- schema: ImageThumbnailSchema,
231
- sourceUrl: sourceUrl ?? url,
232
- }
233
-
234
- if (response.status >= 200 && response.status < 300) {
235
- const contentType: string | undefined = response.headers['content-type']?.toString()
236
- const sourceBuffer = Buffer.from(response.data, 'binary').buffer
237
-
238
- return this.processMedia(sourceBuffer, result, contentType)
239
- }
240
- return result
241
- }
242
-
243
- private async processMedia(sourceBuffer: ArrayBufferLike, imageThumbnail: ImageThumbnail, contentType?: string): Promise<ImageThumbnail> {
244
- const [mediaType, fileType] = contentType?.split('/') ?? ['', '']
245
- imageThumbnail.mime = imageThumbnail.mime ?? {}
246
- imageThumbnail.mime.returned = mediaType
247
-
248
- try {
249
- imageThumbnail.mime.detected = await fileTypeFromBuffer(sourceBuffer as ArrayBuffer)
250
- } catch (ex) {
251
- const error = ex as Error
252
- this.logger?.error(`FileType error: ${error.message}`)
253
- }
254
-
255
- const processImage = async (encoding?: ImageThumbnailEncoding) => {
256
- imageThumbnail.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer)
257
- imageThumbnail.url = await this.createThumbnailDataUrl(sourceBuffer, encoding)
258
- }
259
-
260
- const processVideo = async () => {
261
- // Gracefully handle the case where ffmpeg is not installed.
262
-
263
- if (hasbin.sync('ffmpeg')) {
264
- imageThumbnail.sourceHash = await ImageThumbnailWitness.binaryToSha256(sourceBuffer)
265
- imageThumbnail.url = await this.createThumbnailFromVideo(sourceBuffer)
266
- } else {
267
- imageThumbnail.mime = imageThumbnail.mime ?? {}
268
- imageThumbnail.mime.invalid = true
269
- }
270
- }
271
-
272
- let encoding: ImageThumbnailEncoding = 'PNG'
273
-
274
- switch (fileType.toUpperCase()) {
275
- case 'GIF': {
276
- encoding = 'GIF'
277
- break
278
- }
279
- case 'JPG':
280
- case 'JPEG': {
281
- encoding = 'JPG'
282
- break
283
- }
284
- }
285
-
286
- switch (mediaType) {
287
- case 'image': {
288
- await processImage(encoding)
289
- imageThumbnail.mime.type = mediaType
290
- break
291
- }
292
- case 'video': {
293
- await processVideo()
294
- imageThumbnail.mime.type = mediaType
295
- break
296
- }
297
- default: {
298
- const [detectedMediaType] = imageThumbnail.mime.detected?.mime?.split('/') ?? ['', '']
299
- switch (detectedMediaType) {
300
- case 'image': {
301
- await processImage()
302
- imageThumbnail.mime.type = imageThumbnail.mime.detected?.mime
303
- break
304
- }
305
- case 'video': {
306
- await processVideo()
307
- imageThumbnail.mime.type = imageThumbnail.mime.detected?.mime
308
- break
309
- }
310
- default: {
311
- imageThumbnail.mime.invalid = true
312
- break
313
- }
314
- }
315
- break
316
- }
317
- }
318
- return imageThumbnail
319
- }
320
- }
@@ -1,73 +0,0 @@
1
- import { unlink, writeFile } from 'node:fs/promises'
2
- import { tmpdir } from 'node:os'
3
- import type { WritableOptions } from 'node:stream'
4
- import { Writable } from 'node:stream'
5
-
6
- import ffmpeg from 'fluent-ffmpeg'
7
- import { v4 as uuid } from 'uuid'
8
-
9
- /**
10
- * A Writable stream that collects output from ffmpeg.
11
- */
12
- class FfmpegOutputStream extends Writable {
13
- private readonly chunks: Uint8Array[] = []
14
-
15
- constructor(options?: WritableOptions) {
16
- super(options)
17
- }
18
-
19
- override _write(chunk: never, _encoding: BufferEncoding, callback: (error?: Error | null) => void): void {
20
- this.chunks.push(chunk)
21
- callback()
22
- }
23
-
24
- /**
25
- * Collects the output from ffmpeg into a buffer.
26
- * @returns A buffer containing the concatenated
27
- * output from ffmpeg.
28
- */
29
- toBuffer = () => Buffer.concat(this.chunks)
30
- }
31
-
32
- /**
33
- * Execute FFmpeg using fluent API with provided input buffer and video thumbnail image.
34
- * @param videoBuffer Input video buffer.
35
- * @returns Output buffer containing the video thumbnail image.
36
- */
37
- export const getVideoFrameAsImageFluent = async (videoBuffer: ArrayBufferLike) => {
38
- // Get a temp file name
39
- const tmpFile = `/${tmpdir()}/${uuid()}`
40
- try {
41
- // Write videoBuffer to temp file for use as input to ffmpeg to
42
- // avoid issues with ffmpeg inferring premature EOF from buffer
43
- // passed via stdin (happens when ffmpeg is trying to infer
44
- // input video format)
45
- await writeFile(tmpFile, new Uint8Array(videoBuffer), { encoding: 'binary' })
46
- const imageBuffer = await new Promise<Buffer>((resolve, reject) => {
47
- // Create a Writable stream to collect PNG output from ffmpeg
48
- const ffmpegOutput = new FfmpegOutputStream()
49
- // Execute ffmpeg using fluent API
50
- ffmpeg()
51
- // Uncomment to debug CLI args to ffmpeg
52
- // .on('start', (commandLine) => console.log('Spawned Ffmpeg with command: ' + commandLine))
53
- .on('error', err => reject(err.message))
54
- // Listen for the 'end' event to combine the output into a buffer holding the PNG image
55
- .on('end', () => resolve(ffmpegOutput.toBuffer()))
56
- .input(tmpFile) // Use temp file as input
57
- .takeFrames(1) // Only take 1st video frame
58
- .withNoAudio() // Don't include audio
59
- .outputOptions('-f image2pipe') // Write output to stdout
60
- .videoCodec('png') // Force PNG output
61
- // Start processing and direct ffmpeg stdout to writable stream
62
- .pipe(ffmpegOutput)
63
- })
64
- return imageBuffer
65
- } finally {
66
- // Cleanup temp file
67
- try {
68
- await unlink(tmpFile)
69
- } catch {
70
- // No error here since file doesn't exist
71
- }
72
- }
73
- }
@@ -1 +0,0 @@
1
- export * from './getVideoFrameAsImageFluent.ts'
@@ -1,2 +0,0 @@
1
- export * from './fluent/index.ts'
2
- export * from './spawn/index.ts'
@@ -1,27 +0,0 @@
1
- import { spawn } from 'node:child_process'
2
-
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: Buffer, ffmpegArgs: string[]): Promise<Buffer> => {
10
- return new Promise((resolve, reject) => {
11
- const imageData: Buffer[] = []
12
- const ffmpeg = spawn('ffmpeg', ffmpegArgs)
13
- ffmpeg.stdout.on('data', (data: Buffer) => imageData.push(data))
14
- // TODO: This is required as we're seeing errors thrown due to
15
- // how we're piping the data to ffmpeg. Works perfectly though.
16
- ffmpeg.stdin.on('error', () => {})
17
- ffmpeg.on('close', (code) => {
18
- if (code === 0) {
19
- resolve(Buffer.concat(imageData))
20
- } else {
21
- reject(new Error(`FFmpeg exited with code ${code}`))
22
- }
23
- })
24
- // Pipe the input stream to ffmpeg's stdin
25
- ffmpeg.stdin.end(videoBuffer)
26
- })
27
- }
@@ -1,11 +0,0 @@
1
- import { executeFFmpeg } from './executeFfmpeg.ts'
2
-
3
- /**
4
- * Execute FFmpeg with provided input buffer and return video thumbnail image.
5
- * @param videoBuffer Input video buffer.
6
- * @returns Output buffer containing the video thumbnail image.
7
- */
8
- export const getVideoFrameAsImage = async (videoBuffer: Buffer) => {
9
- const imageBuffer = await executeFFmpeg(videoBuffer, ['-i', 'pipe:', '-ss', '00:00:00', '-vframes', '1', '-f', 'image2pipe', '-'])
10
- return imageBuffer
11
- }
@@ -1,2 +0,0 @@
1
- export * from './executeFfmpeg.ts'
2
- export * from './getVideoFrameAsImage.ts'
@@ -1,3 +0,0 @@
1
- export * from './Config.ts'
2
- export * from './Params.ts'
3
- export * from './Witness.ts'
@@ -1,43 +0,0 @@
1
- import { assertEx } from '@xylabs/assert'
2
-
3
- const allowIpfsIoRepair = true
4
-
5
- /**
6
- * Returns the equivalent IPFS gateway URL for the supplied URL.
7
- * @param urlToCheck The URL to check
8
- * @returns If the supplied URL is an IPFS URL, it converts the URL to the
9
- * equivalent IPFS gateway URL. Otherwise, returns the original URL.
10
- */
11
- export const checkIpfsUrl = (urlToCheck: string, ipfsGateway?: string): string => {
12
- try {
13
- const url = new URL(urlToCheck)
14
- let protocol = url.protocol
15
- let host = url.host
16
- let path = url.pathname
17
- const query = url.search
18
- if (protocol === 'ipfs:') {
19
- protocol = 'https:'
20
- host = assertEx(ipfsGateway, () => 'No ipfsGateway provided')
21
- path = url.host === 'ipfs' ? `ipfs${path}` : `ipfs/${url.host}${path}`
22
- const root = `${protocol}//${host}/${path}`
23
- return query?.length > 0 ? `${root}?${query}` : root
24
- } else if (allowIpfsIoRepair && protocol === 'https' && host === 'ipfs.io') {
25
- protocol = 'https:'
26
- host = assertEx(ipfsGateway, () => 'No ipfsGateway provided')
27
- const pathParts = path.split('/')
28
- if (pathParts[0] === 'ipfs') {
29
- pathParts.shift()
30
- }
31
- path = pathParts.join('/')
32
- const root = `${protocol}//${host}/${path}`
33
- return query?.length > 0 ? `${root}?${query}` : root
34
- } else {
35
- return urlToCheck
36
- }
37
- } catch {
38
- // const error = ex as Error
39
- // console.error(`${error.name}:${error.message} [${urlToCheck}]`)
40
- // console.log(error.stack)
41
- return urlToCheck
42
- }
43
- }
@@ -1,5 +0,0 @@
1
- import { fromByteArray } from 'base64-js'
2
-
3
- export const createDataUrl = (data: ArrayBufferLike, contextType: string, encoding: 'base64' = 'base64') => {
4
- return `data:${contextType};${encoding},${fromByteArray(new Uint8Array(data))}`
5
- }
@@ -1,3 +0,0 @@
1
- export * from './checkIpfsUrl.ts'
2
- export * from './createDataUrl.ts'
3
- export * from './resolveDynamicSvg.ts'
@@ -1,30 +0,0 @@
1
- import type { AxiosResponse } from 'axios'
2
- import axios from 'axios'
3
- import { toByteArray } from 'base64-js'
4
- import { Builder, parseStringPromise } from 'xml2js'
5
-
6
- export const resolveDynamicSvg = async (base64Bytes: string) => {
7
- const decoder = new TextDecoder()
8
- const bytes = toByteArray(base64Bytes)
9
- const svg = decoder.decode(bytes)
10
- const svgObj = await parseStringPromise(svg)
11
- const svgNode = svgObj['svg']
12
- const imageResults = (await Promise.all(
13
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
- svgNode['image'].map(async (img: any) => [
15
- img.$,
16
- await axios.get(img.$.href, { responseType: 'arraybuffer' }),
17
- ]),
18
- )) as [string, AxiosResponse][]
19
- const image = imageResults.map(([href, response]) => {
20
- if (response.data) {
21
- const sourceBuffer = Buffer.from(response.data, 'binary')
22
- return { $: { href: `data:${response.headers['content-type']?.toString()};base64,${sourceBuffer.toString('base64')}` } }
23
- } else {
24
- return { $: { href } }
25
- }
26
- })
27
- const updatedSVG = { ...svgObj, svg: { ...svgNode, image } }
28
- const builder = new Builder()
29
- return builder.buildObject(updatedSVG)
30
- }
package/src/index.ts DELETED
@@ -1,3 +0,0 @@
1
- export { ImageThumbnailPlugin as default, ImageThumbnailPlugin } from './Plugin.ts'
2
- export * from './Witness/index.ts'
3
- export * from '@xyo-network/diviner-image-thumbnail'