feat(aklive2d): use cf pages to store assets (download) [2/2]

This commit is contained in:
Haoyu Xu
2024-08-14 23:14:24 +08:00
parent 0a7ef8531a
commit aaff5fb37f
5 changed files with 103 additions and 14 deletions

View File

@@ -1,18 +1,17 @@
/* eslint-disable no-undef */
import fs from 'fs';
import path from 'path';
import crypto from 'crypto';
import { spawnSync } from 'child_process';
import { readdirSync, fileTypeSync, writeSync } from './file.js';
import { readdirSync, fileTypeSync, writeSync, mkdir, exists } from './file.js';
export default class CFPages {
#uploadPath = path.join(__projectRoot, __config.folder.operator_data);
#downloadPath = path.join(__projectRoot, __config.folder.operator_data);
#gitignorePath = path.join(__projectRoot, '.gitignore');
constructor() {
}
upload() {
const tree = this.#generateDirTree(this.#uploadPath);
async upload() {
const tree = await this.#generateDirTree(this.#uploadPath);
writeSync(JSON.stringify(tree, null), path.join(this.#uploadPath, 'index.json'));
const wrangler = spawnSync('pnpm', ['wrangler', 'pages', 'deploy', this.#uploadPath]);
@@ -21,11 +20,77 @@ export default class CFPages {
console.log('stderr ', wrangler.stderr.toString());
}
download() {
async download() {
const indexFile = `${__config.akassets_url}/index.json`
const resp = await fetch(indexFile);
const data = await resp.json();
mkdir(this.#downloadPath);
let list = data.children.flatMap((child) => {
return this.#generateDownloadList(child, this.#downloadPath);
});
while (list.length > 0) {
const retry = [];
for (const file of list) {
let toDownload = false;
let suppressedPath = file.target.replace(this.#downloadPath, '');
if (exists(file.target)) {
const hash = await this.#getHash(file.target);
if (hash !== file.hash) {
toDownload = true;
} else {
console.log("File already exists and hash matches:", suppressedPath);
}
}
if (!exists(file.target) || toDownload) {
await fetch(file.url)
.then(response => {
return response.arrayBuffer();
})
.then(arraybuffer => {
console.log("Writing to file:", suppressedPath);
writeSync(Buffer.from(arraybuffer), file.target);
return this.#getHash(file.target)
})
.then(hash => {
if (hash !== file.hash) {
throw new Error(`Hash mismatch`);
}
})
.catch(err => {
console.log(`Found error for ${suppressedPath}:`, err)
retry.push(file);
});
}
}
list = retry;
}
}
#generateDirTree(dir) {
#generateDownloadList(data, baseDir, baseUrl = "") {
if (data.type === 'dir') {
let list = [];
const curDir = path.join(baseDir, data.name);
mkdir(curDir);
if (data.children.length > 0) {
for (const child of data.children) {
const filesToDownload = this.#generateDownloadList(child, curDir, baseUrl + data.name + '/');
if (filesToDownload) {
list = [...list, ...filesToDownload];
}
}
return list;
}
} else {
return [{
url: `${__config.akassets_url}/${baseUrl + data.name.replace('#', '%23')}`,
target: path.join(baseDir, data.name),
hash: data.hash
}]
}
return null;
}
async #generateDirTree(dir) {
const files = readdirSync(dir);
let tree = {
name: path.basename(dir),
@@ -39,11 +104,12 @@ export default class CFPages {
const filePath = path.join(dir, file);
const dirType = fileTypeSync(filePath);
if (dirType === 'dir') {
tree.children.push(this.#generateDirTree(filePath))
tree.children.push(await this.#generateDirTree(filePath))
} else {
tree.children.push({
name: file,
type: 'file'
type: 'file',
hash: await this.#getHash(filePath)
});
}
}
@@ -63,4 +129,17 @@ export default class CFPages {
return false;
}
}
#getHash(filePath) {
return new Promise((res, rej) => {
const hash = crypto.createHash('md5');
const rStream = fs.createReadStream(filePath);
rStream.on('data', (data) => {
hash.update(data);
});
rStream.on('end', () => {
res(hash.digest('hex'));
});
})
}
}