Skip to content
This repository was archived by the owner on Oct 4, 2025. It is now read-only.

Commit d0374c7

Browse files
authored
IPFS Upload Tweaks (#5)
* ipfs fixes: - allow reconfiguring auto-js-ipfs with new daemonURL, if provided - enable /ipfs/daemonURL standalone endpoint - use smaller car sizes for uploads - for embed mode, only set page if provided - compute approx. total size of all chunks to be streamed - send size and total size after each chunk is uploaded - update ipfs dependencies, including auto-js-ipfs bump to 0.3.0
1 parent 91f21d7 commit d0374c7

File tree

4 files changed

+133
-68
lines changed

4 files changed

+133
-68
lines changed

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"name": "@webrecorder/awp-sw",
33
"browser": "dist/sw.js",
4+
"version": "0.3.0",
45
"main": "index.js",
56
"type": "module",
67
"repository": {
78
"type": "git",
89
"url": "git+https://github.com/webrecorder/awp-sw.git"
910
},
10-
"version": "0.2.3",
1111
"license": "AGPL-3.0-or-later",
1212
"exports": {
1313
".": "./src/index.js"
@@ -16,14 +16,14 @@
1616
"src/*"
1717
],
1818
"dependencies": {
19-
"@ipld/car": "^5.0.3",
20-
"@ipld/unixfs": "^2.0.0",
19+
"@ipld/car": "^5.1.1",
20+
"@ipld/unixfs": "^2.1.1",
2121
"@webrecorder/wabac": "^2.16.1",
22-
"auto-js-ipfs": "^2.1.1",
22+
"auto-js-ipfs": "^2.2.0",
2323
"client-zip": "^2.3.0",
2424
"hash-wasm": "^4.9.0",
2525
"idb": "^7.1.1",
26-
"p-queue": "^7.3.0",
26+
"p-queue": "^7.3.4",
2727
"uuid": "^9.0.0",
2828
"warcio": "^2.1.0"
2929
},

src/api.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class ExtAPI extends API
2525
"pageTitle": ["c/:coll/pageTitle", "POST"],
2626
"ipfsAdd": ["c/:coll/ipfs", "POST"],
2727
"ipfsRemove": ["c/:coll/ipfs", "DELETE"],
28-
"ipfsDaemonUrl": ["ipfsDaemonUrl", "POST"],
28+
"ipfsDaemonUrl": ["ipfs/daemonUrl", "POST"],
2929
"publicKey": "publicKey",
3030
};
3131
}
@@ -58,6 +58,9 @@ class ExtAPI extends API
5858
case "ipfsRemove":
5959
return await this.ipfsRemove(request, params.coll);
6060

61+
case "ipfsDaemonUrl":
62+
return await this.setIPFSDaemonUrlFromBody(request);
63+
6164
default:
6265
return await super.handleApi(request, params);
6366
}
@@ -100,6 +103,12 @@ class ExtAPI extends API
100103
return {error: "collection_not_found"};
101104
}
102105

106+
const body = await this.setIPFSDaemonUrlFromBody(request);
107+
108+
return {coll, body};
109+
}
110+
111+
async setIPFSDaemonUrlFromBody(request) {
103112
let body;
104113

105114
try {
@@ -111,7 +120,7 @@ class ExtAPI extends API
111120
body = {};
112121
}
113122

114-
return {coll, body};
123+
return body;
115124
}
116125

117126
async startIpfsAdd(event, request, collId) {
@@ -188,17 +197,19 @@ class ExtAPI extends API
188197
// ===========================================================================
189198
async function runIPFSAdd(collId, coll, client, opts, collections, replayOpts) {
190199
let size = 0;
200+
let totalSize = 0;
191201

192202
const sendMessage = (type, result = null) => {
193203
if (client) {
194204
client.postMessage({
195-
type, collId, size, result
205+
type, collId, size, result, totalSize
196206
});
197207
}
198208
};
199209

200-
const {url, cid} = await ipfsAdd(coll, opts, replayOpts, (incSize) => {
210+
const {url, cid} = await ipfsAdd(coll, opts, replayOpts, (incSize, _totalSize) => {
201211
size += incSize;
212+
totalSize = _totalSize;
202213
sendMessage("ipfsProgress");
203214
});
204215

src/ipfsutils.js

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
1-
import { initAutoIPFS } from "@webrecorder/wabac/src/ipfs.js";
21
import { Downloader } from "./downloader.js";
32

3+
import { create as createAutoIPFS } from "auto-js-ipfs";
4+
45
import * as UnixFS from "@ipld/unixfs";
56
import { CarWriter } from "@ipld/car";
67
import Queue from "p-queue";
78

89
// eslint-disable-next-line no-undef
910
const autoipfsOpts = {web3StorageToken: __WEB3_STORAGE_TOKEN__};
1011

12+
let autoipfs = null;
13+
1114
export async function setAutoIPFSUrl(url) {
15+
if (autoipfsOpts.daemonURL !== url) {
16+
autoipfs = null;
17+
}
1218
autoipfsOpts.daemonURL = url;
1319
}
1420

1521
export async function ipfsAdd(coll, downloaderOpts = {}, replayOpts = {}, progress = null) {
16-
const autoipfs = await initAutoIPFS(autoipfsOpts);
22+
if (!autoipfs) {
23+
autoipfs = await createAutoIPFS(autoipfsOpts);
24+
}
1725

18-
const filename = "webarchive.wacz";
26+
const filename = replayOpts.filename || "webarchive.wacz";
1927

2028
if (replayOpts.customSplits) {
2129
const ZIP = new Uint8Array([]);
@@ -27,7 +35,7 @@ export async function ipfsAdd(coll, downloaderOpts = {}, replayOpts = {}, progre
2735
const gzip = replayOpts.gzip !== undefined ? replayOpts.gzip : true;
2836

2937
const dl = new Downloader({...downloaderOpts, coll, filename, gzip});
30-
const dlResponse = await dl.download(progress);
38+
const dlResponse = await dl.download();
3139

3240
if (!coll.config.metadata.ipfsPins) {
3341
coll.config.metadata.ipfsPins = [];
@@ -44,9 +52,10 @@ export async function ipfsAdd(coll, downloaderOpts = {}, replayOpts = {}, progre
4452
capacity = 1048576 * 200;
4553
} else {
4654
concur = 3;
47-
shardSize = 1024 * 1024 * 10;
55+
shardSize = 1024 * 1024 * 5;
4856
// use default capacity
49-
capacity = undefined;
57+
// capacity = undefined;
58+
capacity = 1048576 * 200;
5059
}
5160

5261
const { readable, writable } = new TransformStream(
@@ -65,6 +74,17 @@ export async function ipfsAdd(coll, downloaderOpts = {}, replayOpts = {}, progre
6574
console.warn("Couldn't load favicon");
6675
}
6776

77+
const htmlContent = getReplayHtml(dlResponse.filename, replayOpts);
78+
79+
let totalSize = 0;
80+
81+
if (coll.config && coll.config.metadata && coll.config.metadata.size) {
82+
totalSize = coll.config.metadata.size +
83+
swContent.length + uiContent.length + favicon.length + htmlContent.length;
84+
}
85+
86+
progress(0, totalSize);
87+
6888
let url, cid;
6989

7090
const p = readable
@@ -73,16 +93,21 @@ export async function ipfsAdd(coll, downloaderOpts = {}, replayOpts = {}, progre
7393
.pipeTo(
7494
new WritableStream({
7595
write: (res) => {
76-
url = res.url;
77-
cid = res.cid;
96+
if (res.url && res.cid) {
97+
url = res.url;
98+
cid = res.cid;
99+
}
100+
if (res.size) {
101+
progress(res.size, totalSize);
102+
}
78103
},
79104
})
80105
);
81106

82107
ipfsGenerateCar(
83108
writable,
84109
dlResponse.filename, dlResponse.body,
85-
swContent, uiContent, replayOpts,
110+
swContent, uiContent, htmlContent, replayOpts,
86111
downloaderOpts.markers, favicon,
87112
);
88113

@@ -98,7 +123,9 @@ export async function ipfsAdd(coll, downloaderOpts = {}, replayOpts = {}, progre
98123
}
99124

100125
export async function ipfsRemove(coll) {
101-
const autoipfs = await initAutoIPFS(autoipfsOpts);
126+
if (!autoipfs) {
127+
autoipfs = await createAutoIPFS(autoipfsOpts);
128+
}
102129

103130
if (coll.config.metadata.ipfsPins) {
104131

@@ -138,16 +165,14 @@ async function ipfsWriteBuff(writer, name, content, dir) {
138165

139166
// ===========================================================================
140167
export async function ipfsGenerateCar(writable, waczPath,
141-
waczContent, swContent, uiContent, replayOpts, markers, favicon) {
168+
waczContent, swContent, uiContent, htmlContent, replayOpts, markers, favicon) {
142169

143170
const writer = UnixFS.createWriter({ writable });
144171

145172
const rootDir = UnixFS.createDirectoryWriter(writer);
146173

147174
const encoder = new TextEncoder();
148175

149-
const htmlContent = getReplayHtml(waczPath, replayOpts);
150-
151176
await ipfsWriteBuff(writer, "ui.js", uiContent, rootDir);
152177

153178
if (replayOpts.showEmbed) {
@@ -388,7 +413,7 @@ function getReplayHtml(waczPath, replayOpts = {}) {
388413
</style>
389414
</head>
390415
<body>${showEmbed ? `
391-
<replay-web-page ${deepLink ? "deepLink=\"true\" " : ""}url="${pageUrl}" loading="${loading || ""}" embed="replay-with-info" src="${waczPath}"></replay-web-page>` : `
416+
<replay-web-page ${deepLink ? "deepLink=\"true\" " : ""} ${pageUrl ? `url="${pageUrl}"` : ""} loading="${loading || ""}" embed="replay-with-info" src="${waczPath}"></replay-web-page>` : `
392417
<replay-app-main source="${waczPath}"></replay-app-main>`
393418
}
394419
</body>
@@ -399,9 +424,6 @@ function getReplayHtml(waczPath, replayOpts = {}) {
399424

400425
// Copied from https://github.com/web3-storage/w3protocol/blob/main/packages/upload-client/src/sharding.js
401426

402-
const SHARD_SIZE = 1024 * 1024 * 10;
403-
const CONCURRENT_UPLOADS = 3;
404-
405427
/**
406428
* Shard a set of blocks into a set of CAR files. The last block is assumed to
407429
* be the DAG root and becomes the CAR root CID for the last CAR output.
@@ -412,36 +434,45 @@ export class ShardingStream extends TransformStream {
412434
/**
413435
* @param {import('./types').ShardingOptions} [options]
414436
*/
415-
constructor(shardSize = SHARD_SIZE) {
437+
constructor(shardSize) {
416438
/** @type {import('@ipld/unixfs').Block[]} */
417439
let shard = [];
418440
/** @type {import('@ipld/unixfs').Block[] | null} */
419441
let readyShard = null;
420-
let size = 0;
442+
let readySize = 0;
443+
444+
let currSize = 0;
421445

422446
super({
423447
async transform(block, controller) {
424448
if (readyShard != null) {
425-
controller.enqueue(await encodeBlocks(readyShard));
449+
const blocks = await encodeBlocks(readyShard);
450+
const size = readySize;
451+
controller.enqueue({blocks, size});
426452
readyShard = null;
427453
}
428-
if (shard.length && size + block.bytes.length > shardSize) {
454+
if (shard.length && currSize + block.bytes.length > shardSize) {
429455
readyShard = shard;
456+
readySize = currSize;
430457
shard = [];
431-
size = 0;
458+
currSize = 0;
432459
}
433460
shard.push(block);
434-
size += block.bytes.length;
461+
currSize += block.bytes.length;
435462
},
436463

437464
async flush(controller) {
438465
if (readyShard != null) {
439-
controller.enqueue(await encodeBlocks(readyShard));
466+
const blocks = await encodeBlocks(readyShard);
467+
const size = readySize;
468+
controller.enqueue({blocks, size});
440469
}
441470

442471
const rootBlock = shard.at(-1);
443472
if (rootBlock != null) {
444-
controller.enqueue(await encodeBlocks(shard, rootBlock.cid));
473+
const blocks = await encodeBlocks(shard, rootBlock.cid);
474+
const size = currSize;
475+
controller.enqueue({blocks, size});
445476
}
446477
},
447478
});
@@ -460,19 +491,20 @@ export class ShardingStream extends TransformStream {
460491
* @extends {TransformStream<import('./types').CARFile, import('./types').CARMetadata>}
461492
*/
462493
export class ShardStoringStream extends TransformStream {
463-
constructor(autoipfs, concurrency = CONCURRENT_UPLOADS) {
494+
constructor(autoipfs, concurrency) {
464495
const queue = new Queue({ concurrency });
465496
const abortController = new AbortController();
466497
super({
467-
async transform(car, controller) {
498+
async transform({blocks, size}, controller) {
468499
void queue.add(
469500
async () => {
470501
try {
471-
//const opts = { ...options, signal: abortController.signal };
472-
//const cid = await add(conf, car, opts)
473-
const resUrls = await autoipfs.uploadCAR(car);
502+
const cid = blocks.roots[0];
503+
504+
const resUrls = await autoipfs.uploadCAR(blocks);
505+
const url = resUrls[0];
474506

475-
controller.enqueue({cid: car.roots[0], url: resUrls[0]});
507+
controller.enqueue({cid, url, size});
476508

477509
//const { version, roots, size } = car
478510
//controller.enqueue({ version, roots, cid, size })

0 commit comments

Comments
 (0)