add more metadata to IPFSUploader

This commit is contained in:
Siddharth Singh 2024-10-27 07:35:29 +05:30
parent 9b473406cf
commit 0058437170
No known key found for this signature in database
3 changed files with 67 additions and 2 deletions

View file

@ -41,6 +41,7 @@
"@gfx/canvas-wasm": "jsr:@gfx/canvas-wasm@^0.4.2", "@gfx/canvas-wasm": "jsr:@gfx/canvas-wasm@^0.4.2",
"@hono/hono": "jsr:@hono/hono@^4.4.6", "@hono/hono": "jsr:@hono/hono@^4.4.6",
"@isaacs/ttlcache": "npm:@isaacs/ttlcache@^1.4.1", "@isaacs/ttlcache": "npm:@isaacs/ttlcache@^1.4.1",
"@jcayzac/image-information": "npm:@jcayzac/image-information@1.1.1",
"@lambdalisue/async": "jsr:@lambdalisue/async@^2.1.1", "@lambdalisue/async": "jsr:@lambdalisue/async@^2.1.1",
"@negrel/webpush": "jsr:@negrel/webpush@^0.3.0", "@negrel/webpush": "jsr:@negrel/webpush@^0.3.0",
"@noble/secp256k1": "npm:@noble/secp256k1@^2.0.0", "@noble/secp256k1": "npm:@noble/secp256k1@^2.0.0",
@ -59,6 +60,7 @@
"@std/json": "jsr:@std/json@^0.223.0", "@std/json": "jsr:@std/json@^0.223.0",
"@std/media-types": "jsr:@std/media-types@^0.224.1", "@std/media-types": "jsr:@std/media-types@^0.224.1",
"@std/streams": "jsr:@std/streams@^0.223.0", "@std/streams": "jsr:@std/streams@^0.223.0",
"blurhash": "npm:blurhash@2.0.5",
"comlink": "npm:comlink@^4.4.1", "comlink": "npm:comlink@^4.4.1",
"comlink-async-generator": "npm:comlink-async-generator@^0.0.1", "comlink-async-generator": "npm:comlink-async-generator@^0.0.1",
"commander": "npm:commander@12.1.0", "commander": "npm:commander@12.1.0",

28
deno.lock generated
View file

@ -80,12 +80,14 @@
"jsr:@std/streams@0.223": "0.223.0", "jsr:@std/streams@0.223": "0.223.0",
"npm:@electric-sql/pglite@~0.2.8": "0.2.8", "npm:@electric-sql/pglite@~0.2.8": "0.2.8",
"npm:@isaacs/ttlcache@^1.4.1": "1.4.1", "npm:@isaacs/ttlcache@^1.4.1": "1.4.1",
"npm:@jcayzac/image-information@1.1.1": "1.1.1",
"npm:@noble/hashes@^1.4.0": "1.4.0", "npm:@noble/hashes@^1.4.0": "1.4.0",
"npm:@noble/secp256k1@2": "2.1.0", "npm:@noble/secp256k1@2": "2.1.0",
"npm:@scure/base@^1.1.6": "1.1.6", "npm:@scure/base@^1.1.6": "1.1.6",
"npm:@scure/bip32@^1.4.0": "1.4.0", "npm:@scure/bip32@^1.4.0": "1.4.0",
"npm:@scure/bip39@^1.3.0": "1.3.0", "npm:@scure/bip39@^1.3.0": "1.3.0",
"npm:@types/node@*": "18.16.19", "npm:@types/node@*": "18.16.19",
"npm:blurhash@2.0.5": "2.0.5",
"npm:comlink-async-generator@*": "0.0.1", "npm:comlink-async-generator@*": "0.0.1",
"npm:comlink-async-generator@^0.0.1": "0.0.1", "npm:comlink-async-generator@^0.0.1": "0.0.1",
"npm:comlink@^4.4.1": "4.4.1", "npm:comlink@^4.4.1": "4.4.1",
@ -658,6 +660,12 @@
"@isaacs/ttlcache@1.4.1": { "@isaacs/ttlcache@1.4.1": {
"integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==" "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA=="
}, },
"@jcayzac/image-information@1.1.1": {
"integrity": "sha512-WXM5RTu3tTuAPXPx4ytbywjqTxnjBUWM1sY+7A9UPqPwQbM9V3jkY/rzo1OJ6VYSpogFdK4cyc+o9FZFZLW+nw==",
"dependencies": [
"image-size"
]
},
"@noble/ciphers@0.5.3": { "@noble/ciphers@0.5.3": {
"integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==" "integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w=="
}, },
@ -769,6 +777,9 @@
"bintrees@1.0.2": { "bintrees@1.0.2": {
"integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==" "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw=="
}, },
"blurhash@2.0.5": {
"integrity": "sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w=="
},
"braces@3.0.2": { "braces@3.0.2": {
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dependencies": [ "dependencies": [
@ -987,6 +998,15 @@
"safer-buffer" "safer-buffer"
] ]
}, },
"image-size@1.1.1": {
"integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==",
"dependencies": [
"queue"
]
},
"inherits@2.0.4": {
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"is-fullwidth-code-point@4.0.0": { "is-fullwidth-code-point@4.0.0": {
"integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==" "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="
}, },
@ -1260,6 +1280,12 @@
"punycode@2.3.1": { "punycode@2.3.1": {
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
}, },
"queue@6.0.2": {
"integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
"dependencies": [
"inherits"
]
},
"restore-cursor@4.0.0": { "restore-cursor@4.0.0": {
"integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
"dependencies": [ "dependencies": [
@ -2104,12 +2130,14 @@
"jsr:@std/streams@0.223", "jsr:@std/streams@0.223",
"npm:@electric-sql/pglite@~0.2.8", "npm:@electric-sql/pglite@~0.2.8",
"npm:@isaacs/ttlcache@^1.4.1", "npm:@isaacs/ttlcache@^1.4.1",
"npm:@jcayzac/image-information@1.1.1",
"npm:@noble/secp256k1@2", "npm:@noble/secp256k1@2",
"npm:@scure/base@^1.1.6", "npm:@scure/base@^1.1.6",
"npm:comlink-async-generator@^0.0.1", "npm:comlink-async-generator@^0.0.1",
"npm:comlink@^4.4.1", "npm:comlink@^4.4.1",
"npm:commander@12.1.0", "npm:commander@12.1.0",
"npm:entities@^4.5.0", "npm:entities@^4.5.0",
"npm:fast-blurhash@1.1.4",
"npm:fast-stable-stringify@1", "npm:fast-stable-stringify@1",
"npm:formdata-helper@0.3", "npm:formdata-helper@0.3",
"npm:hono-rate-limiter@0.3", "npm:hono-rate-limiter@0.3",

View file

@ -1,5 +1,11 @@
import { NUploader } from '@nostrify/nostrify'; import { NUploader } from '@nostrify/nostrify';
import { z } from 'zod'; import { z } from 'zod';
import { probe } from '@jcayzac/image-information';
import { Stickynotes } from '@soapbox/stickynotes';
import { encode } from 'blurhash';
import { encodeHex } from '@std/encoding/hex';
const console = new Stickynotes('ditto:ipfs:uploader');
export interface IPFSUploaderOpts { export interface IPFSUploaderOpts {
baseUrl: string; baseUrl: string;
@ -7,6 +13,19 @@ export interface IPFSUploaderOpts {
fetch?: typeof fetch; fetch?: typeof fetch;
} }
function toByteArray(f: File): Promise<Uint8Array> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.addEventListener('loadend', (m) => {
if (m?.target?.result instanceof ArrayBuffer) {
resolve(new Uint8Array(m.target.result));
} else reject('Error loading file: readAsArrayBufferFailed');
});
reader.addEventListener('error', (e) => reject(e));
reader.readAsArrayBuffer(f);
});
}
/** /**
* IPFS uploader. It expects an IPFS node up and running. * IPFS uploader. It expects an IPFS node up and running.
* It will try to connect to `http://localhost:5001` by default, * It will try to connect to `http://localhost:5001` by default,
@ -36,13 +55,29 @@ export class IPFSUploader implements NUploader {
}); });
const { Hash: cid } = IPFSUploader.schema().parse(await response.json()); const { Hash: cid } = IPFSUploader.schema().parse(await response.json());
const tags: [['url', string], ...string[][]] = [
return [
['url', new URL(`/ipfs/${cid}`, this.baseUrl).toString()], ['url', new URL(`/ipfs/${cid}`, this.baseUrl).toString()],
['m', file.type], ['m', file.type],
['cid', cid], ['cid', cid],
['size', file.size.toString()], ['size', file.size.toString()],
]; ];
try {
const buffer = await toByteArray(file);
const hash = await crypto.subtle.digest('SHA-256', buffer).then(encodeHex);
tags.push(['x', hash], ['ox', hash]);
const metadata = probe(buffer);
if (metadata) {
// sane default from https://github.com/woltapp/blurhash readme
const blurhash = encode(new Uint8ClampedArray(buffer), metadata.width, metadata.height, 4, 4);
tags.push(['blurhash', blurhash]);
tags.push(['dim', `${metadata.width}x${metadata.height}`]);
}
} catch (e) {
console.error(`Error parsing ipfs metadata: ${e}`);
}
return tags;
} }
async delete(cid: string, opts?: { signal?: AbortSignal }): Promise<void> { async delete(cid: string, opts?: { signal?: AbortSignal }): Promise<void> {