const fs = require('fs'); const path = require('path'); /** 清洗 Fandom CDN 缩略图链接,去掉 scale-to-width-down 参数还原原图地址 */ function cleanImageUrl(url) { return url.replace(/(\/scale-to-width-down\/\d+\?.*)$/, ''); } /** 从图片 URL 提取文件名并规范化(小写、下划线转短横线) */ function generateImageName(url) { const fileNameMatch = url.match(/\/([^/]+)\.png/i); if (fileNameMatch && fileNameMatch[1]) { // 去除 .、&、(、) 等特殊字符 return fileNameMatch[1].toLowerCase().replace(/_/g, '-').replaceAll(/\.|%26|%28|%29|%e2%80%a2/g, "").replace(/-{2,}/g, '-'); } return null; } /** 对 Wiki 原始文件名应用站点自定义转换,得到最终保存用的基础名 */ function toOutputBaseName(rawName, nameTransform) { const transformed = nameTransform ? nameTransform(rawName) : rawName; return transformed; } /** 根据 Fandom 站点根地址和页面标题拼接 parse API 请求 URL */ function buildParseApiUrl(baseUrl, wikiTitle) { const apiBase = baseUrl.replace(/\/$/, ''); const params = new URLSearchParams({ action: 'parse', page: wikiTitle, prop: 'text', format: 'json', }); return `${apiBase}/api.php?${params.toString()}`; } /** 判断图片 URL 是否为懒加载占位图 */ function isPlaceholderImageUrl(url) { return !url || url.startsWith('data:image/gif'); } /** 从 img 元素读取图片地址,src 为占位图时自动回退到 data-src */ function resolveImageUrl($img, imageUrlAttr = 'src') { const primary = $img.attr(imageUrlAttr); if (!isPlaceholderImageUrl(primary)) { return primary; } if (imageUrlAttr === 'src') { const fallback = $img.attr('data-src'); if (!isPlaceholderImageUrl(fallback)) { return fallback; } } return null; } /** 延迟指定毫秒,用于控制下载请求间隔 */ function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } /** 从图片路径提取不含扩展名的文件名,兼容绝对 URL 与相对路径 */ function getFileBaseNameFromUrl(url) { const normalized = url.split('?')[0].split('#')[0]; const fileName = path.basename(normalized); return path.basename(fileName, path.extname(fileName)); } /** 读取本地保存目录中所有 .webp 角色的基础文件名列表 */ function listLocalCharacters(savePath) { if (!fs.existsSync(savePath)) { return []; } return fs .readdirSync(savePath) .filter((file) => file.endsWith('.webp')) .map((file) => path.basename(file, '.webp')) .sort(); } module.exports = { cleanImageUrl, generateImageName, toOutputBaseName, buildParseApiUrl, isPlaceholderImageUrl, resolveImageUrl, getFileBaseNameFromUrl, listLocalCharacters, sleep, };