kt-template-online-api/src/qqbot/plugins/bangDream/tsugu/data-clients/asset-cache-client.ts

117 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as path from 'path';
import {
getCacheDirectory,
getFileNameFromUrl,
} from '@/qqbot/plugins/bangDream/tsugu/data-clients/cache-path';
import { download } from '@/qqbot/plugins/bangDream/tsugu/data-clients/file-cache-client';
import { Buffer } from 'buffer';
import { assetErrorImageBuffer } from '@/qqbot/plugins/bangDream/tsugu/canvas/image-utils';
import { logger } from '@/qqbot/plugins/bangDream/tsugu/runtime/logger';
import * as fs from 'fs';
import {
BANGDREAM_MISSING_URL_CACHE_EXPIRY_MS,
getCacheClientErrorMessage,
getCacheClientResponseStatus,
runWithCacheClientRetry,
} from '@/qqbot/plugins/bangDream/tsugu/data-clients/cache-client-policy';
const errUrl: { [key: string]: number } = {};
const memoryCache: { [url: string]: Buffer } = {};
/**
* 在数据下载与缓存层中下载File。
*
* @param url - 远端资源地址。
* @param IgnoreErr - IgnoreErr参数未传入时使用默认值。
* @param overwrite - overwrite参数未传入时使用默认值。
* @param retryCount - retryCount参数未传入时使用默认值。
* @returns 异步处理结果。
*/
async function downloadFile(
url: string,
IgnoreErr: boolean = true,
overwrite = false,
retryCount = 3,
): Promise<Buffer> {
try {
const currentTime = Date.now();
if (url.includes('undefined')) {
throw new Error("downloadFile: url.includes('undefined')");
}
if (
errUrl[url] &&
currentTime - errUrl[url] < BANGDREAM_MISSING_URL_CACHE_EXPIRY_MS
) {
throw new Error('downloadFile: errUrl includes url and not expired');
}
const cacheTime = overwrite ? 0 : 1 / 0;
const cacheDir = getCacheDirectory(url);
const fileName = getFileNameFromUrl(url);
let assetNotExists = false;
return await runWithCacheClientRetry({
action: async () => {
assetNotExists = false;
const data = await download(url, cacheDir, fileName, cacheTime);
const htmlSig = Buffer.from('<!DOCTYPE html>');
const slice = Buffer.from(data.subarray(0, htmlSig.length));
if (slice.equals(htmlSig)) {
fs.unlinkSync(path.join(cacheDir, fileName));
assetNotExists = true;
throw new Error(
'downloadFile: data.toString().startsWith("<!DOCTYPE html>")',
);
}
return data;
},
onRetry: (nextAttempt, normalizedRetryCount) =>
logger(
`downloader`,
`Retrying download for "${url}" (attempt ${nextAttempt}/${normalizedRetryCount})`,
),
retryCount,
shouldRetry: () => !assetNotExists,
});
} catch (e) {
logger(
`downloader`,
`Failed to download file from "${url}". Error: ${getCacheClientErrorMessage(e)}`,
);
if (getCacheClientResponseStatus(e) === 404) {
errUrl[url] = Date.now();
}
if ((url.includes('.png') || url.includes('.svg')) && IgnoreErr) {
return assetErrorImageBuffer;
}
throw e; // 抛出错误
}
}
/**
* 在数据下载与缓存层中下载File缓存。
*
* @param url - 远端资源地址。
* @param IgnoreErr - IgnoreErr参数未传入时使用默认值。
* @returns 异步处理结果。
*/
async function downloadFileCache(
url: string,
IgnoreErr = true,
): Promise<Buffer> {
if (memoryCache[url]) {
return memoryCache[url];
}
const data = await downloadFile(url, IgnoreErr);
if (process.env.MEMORY_CACHE == 'true') {
memoryCache[url] = data;
}
return data;
}
export { downloadFile, downloadFileCache };