feedback5 WH: 自选 art — fetch-assets plans every data/backups.json unit research 07 lacks (the 72 owned 6★: avatar / portrait with E2, battle Spine Front / Back, every skill icon and skill sound, sub-profession icon, from 07's URL patterns), the 自选 summons as tokens, and a type icon per module of chess.json / backups.json (manifest modules, the loadout tiles' fallback); --add-only downloads only missing files and never rewrites, re-downloads or deletes an existing one (worktrees sharing public/assets)
Downloaded 1,295 files, 127.2 MB (133,426,089 bytes) into the shared public/assets; no existing file changed or removed. data/assets.json only gains entries: 72 chars (all with avatar, portrait, Front and Back Spine), 38 tokens (avatars; no Spine — upstream has skin variants only), 208 skill icons, 10 sub-profession icons, 102 SFX units, 131 module type icons.
This commit is contained in:
1 parent
42920c62c5
commit
9db99aad00
7 files changed
+231
-32
No files matched your search
@@ -10,7 +10,8 @@
|
||||
// - Idempotent: an existing file is kept when its size matches the ledger entry
|
||||
// of a previous download or the expected byte count from research, or (when
|
||||
// neither is known) when it passes format validation. The ledger lives in
|
||||
// .cache/assets-ledger.json.
|
||||
// .cache/assets-ledger.json. `keepExisting` (fetch-assets --add-only) keeps
|
||||
// every existing file as it is; `written` = the files this run wrote.
|
||||
|
||||
import { mkdir, readFile, rename, stat, unlink, writeFile } from 'node:fs/promises';
|
||||
import { dirname, join } from 'node:path';
|
||||
@@ -39,17 +40,21 @@ export class Downloader {
|
||||
* @param {number} [o.retries]
|
||||
* @param {number} [o.timeoutMs]
|
||||
* @param {boolean} [o.force] re-download even when files exist
|
||||
* @param {boolean} [o.keepExisting] never re-download or rewrite an existing file (a checkout sharing public/assets)
|
||||
* @param {(msg:string)=>void} [o.log]
|
||||
* @param {typeof fetch} [o.fetchImpl]
|
||||
* @param {number} [o.backoffMs] base retry delay (doubles per attempt)
|
||||
*/
|
||||
constructor({ root, ledgerPath, concurrency = 16, retries = 3, timeoutMs = 120000, force = false, log = console.log, fetchImpl = globalThis.fetch, backoffMs = 400 }) {
|
||||
constructor({ root, ledgerPath, concurrency = 16, retries = 3, timeoutMs = 120000, force = false, keepExisting = false, log = console.log, fetchImpl = globalThis.fetch, backoffMs = 400 }) {
|
||||
this.root = root;
|
||||
this.ledgerPath = ledgerPath;
|
||||
this.concurrency = Math.max(1, Math.min(64, Number(concurrency) || 16));
|
||||
this.retries = Math.max(1, Number(retries) || 3);
|
||||
this.timeoutMs = timeoutMs;
|
||||
this.force = force;
|
||||
this.force = force && !keepExisting;
|
||||
this.keepExisting = !!keepExisting;
|
||||
/** @type {Set<string>} the files (paths under root) this run wrote */
|
||||
this.written = new Set();
|
||||
this.log = log;
|
||||
this.fetch = fetchImpl;
|
||||
this.backoffMs = Math.max(0, Number(backoffMs) || 0);
|
||||
@@ -87,6 +92,7 @@ export class Downloader {
|
||||
let st;
|
||||
try { st = await stat(abs); } catch { return -1; }
|
||||
if (!st.isFile() || st.size <= 0) return -1;
|
||||
if (this.keepExisting) return st.size;
|
||||
const led = this.ledger.files[job.rel];
|
||||
if (job.mutable) {
|
||||
// Post-processed files (atlases) change size; validate content instead.
|
||||
@@ -162,6 +168,7 @@ export class Downloader {
|
||||
return { status: 'error', bytes: 0, error: `write failed: ${e.message}` };
|
||||
}
|
||||
this.ledger.files[job.rel] = { url: src, bytes: r.buf.length };
|
||||
this.written.add(job.rel);
|
||||
this.dirty++;
|
||||
const sizeChanged = !!(job.bytes && job.bytes !== r.buf.length && !job.mutable);
|
||||
return { status: 'ok', bytes: r.buf.length, url: src, sizeChanged };
|
||||
|
||||
Reference in new issue
Block a user