import { mkdir, readFile, stat, writeFile } from 'node:fs/promises' import path from 'node:path' const PRESETS_FILE = path.join(process.cwd(), 'src/lib/3d/material-pbr-presets.ts') const OUTPUT_ROOT = path.join(process.cwd(), 'public/materials/pbr/polyhaven') const URL_PATTERN = /https:\/\/(?:dl\.polyhaven\.org|polyhaven\.com|cdn\.polyhaven\.com)\/[^\s'"]+/g function toLocalFilePath(remoteUrl: string): string | null { try { const parsed = new URL(remoteUrl) const segments = parsed.pathname.split('/').filter(Boolean) const fileName = segments.at(-1) if (!fileName) return null const assetName = segments.at(-2) || 'misc' return path.join(OUTPUT_ROOT, assetName, fileName) } catch { return null } } async function exists(filePath: string): Promise { try { await stat(filePath) return true } catch { return false } } async function downloadTexture(remoteUrl: string, localFilePath: string): Promise { const response = await fetch(remoteUrl) if (!response.ok) { throw new Error(`下载失败 ${response.status}: ${remoteUrl}`) } const buffer = Buffer.from(await response.arrayBuffer()) await mkdir(path.dirname(localFilePath), { recursive: true }) await writeFile(localFilePath, buffer) } async function main() { const source = await readFile(PRESETS_FILE, 'utf8') const matches = source.match(URL_PATTERN) || [] const remoteUrls = Array.from(new Set(matches)) if (remoteUrls.length === 0) { console.log('未发现需要同步的远程贴图 URL。') return } let downloaded = 0 let skipped = 0 let failed = 0 for (const remoteUrl of remoteUrls) { const localFilePath = toLocalFilePath(remoteUrl) if (!localFilePath) { failed += 1 console.warn(`无法解析路径: ${remoteUrl}`) continue } if (await exists(localFilePath)) { skipped += 1 continue } try { await downloadTexture(remoteUrl, localFilePath) downloaded += 1 console.log(`已下载: ${path.relative(process.cwd(), localFilePath)}`) } catch (error) { failed += 1 console.warn(`下载失败: ${remoteUrl}`) console.warn(error) } } console.log(`同步完成:下载 ${downloaded},跳过 ${skipped},失败 ${failed}`) } main().catch((error) => { console.error('贴图同步脚本执行失败', error) process.exit(1) })