splatoon3.ink/app/data/ImageProcessor.mjs
Matt Isenhower 41c9f9a315 Add VFS layer to avoid downloading files only needed for existence checks
Uses S3 ListObjectsV2 to build an in-memory file listing at startup,
allowing exists() and olderThan() to resolve from the listing instead
of requiring files on disk. sync:download now skips assets/splatnet/,
data/xrank/, data/festivals.ranking.*, and status-screenshots/.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 09:26:05 -08:00

81 lines
2.0 KiB
JavaScript

import fs from 'fs/promises';
import path from 'path';
import PQueue from 'p-queue';
import prefixedConsole from '../common/prefixedConsole.mjs';
import { normalizeSplatnetResourcePath } from '../common/util.mjs';
import { exists, mkdirp } from '../common/fs.mjs';
import vfs from '../common/vfs.mjs';
const queue = new PQueue({ concurrency: 4 });
export default class ImageProcessor
{
destinationDirectory = 'dist';
outputDirectory = 'assets/splatnet';
constructor() {
this.console = prefixedConsole('Images');
this.siteUrl = process.env.SITE_URL;
}
async process(url, defer = true) {
// Normalize the path
let destination = this.normalize(url);
// Download the image if necessary
let job = () => this.maybeDownload(url, destination);
// defer ? queue.add(job) : await job();
if (defer) {
queue.add(job);
} else {
await job();
}
// Return the new public URL
return [destination, this.publicUrl(destination)];
}
static onIdle() {
return queue.onIdle();
}
normalize(url) {
return normalizeSplatnetResourcePath(url);
}
localPath(file) {
return `${this.destinationDirectory}/${this.outputDirectory}/${file}`;
}
publicUrl(file) {
return `${this.siteUrl ?? ''}/${this.outputDirectory}/${file}`;
}
async maybeDownload(url, destination) {
// If the file already exists, we don't need to download it again
if (await exists(this.localPath(destination))) {
return;
}
return await this.download(url, destination);
}
async download(url, destination) {
this.console.info(`Downloading image: ${destination}`);
try {
let result = await fetch(url);
if (!result.ok) {
throw new Error(`Invalid image response code: ${result.status}`);
}
await mkdirp(path.dirname(this.localPath(destination)));
await fs.writeFile(this.localPath(destination), result.body);
vfs.track(this.localPath(destination));
} catch (e) {
this.console.error(`Image download failed for ${destination}`, e);
}
}
}