chore: 初始化仓库
中华文明全图鉴——文物全图系统(PC Web 地图 + NestJS API + 管理后台)。 含三大 IP(文物南迁北归 / 国宝海外回归 / 博物馆手艺人)、AI 文物对话、 文物地图与详情、以及 demo-video-kit 演示视频生成工具。
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
// 校对本地已下载的文物图片:识别真实图片类型、规范扩展名,并把 DB image_url 指向本地路径。
|
||||
import { Pool } from "pg";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const PUBLIC_DIR = path.resolve(__dirname, "../../apps/web/public/artifacts");
|
||||
const pool = new Pool({
|
||||
connectionString:
|
||||
process.env.DATABASE_URL ?? "postgresql://postgres@localhost:5432/wenwumap",
|
||||
});
|
||||
|
||||
function detectExt(buf) {
|
||||
if (buf.length < 12) return null;
|
||||
if (buf[0] === 0xff && buf[1] === 0xd8) return "jpg";
|
||||
if (buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47) return "png";
|
||||
if (buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46) return "gif";
|
||||
if (buf.slice(0, 4).toString() === "RIFF" && buf.slice(8, 12).toString() === "WEBP") return "webp";
|
||||
if (buf.slice(0, 5).toString().includes("<svg") || buf.slice(0, 5).toString().includes("<?xml")) return "svg";
|
||||
return null;
|
||||
}
|
||||
|
||||
const main = async () => {
|
||||
if (!fs.existsSync(PUBLIC_DIR)) {
|
||||
console.log("无本地图片目录");
|
||||
return;
|
||||
}
|
||||
const files = fs.readdirSync(PUBLIC_DIR);
|
||||
const { rows } = await pool.query(
|
||||
"SELECT id, image_url FROM artifacts WHERE image_url IS NOT NULL ORDER BY unified_map_id"
|
||||
);
|
||||
|
||||
let ok = 0;
|
||||
let bad = 0;
|
||||
for (const { id } of rows) {
|
||||
const match = files.find((f) => f.startsWith(id));
|
||||
if (!match) continue;
|
||||
const full = path.join(PUBLIC_DIR, match);
|
||||
const stat = fs.statSync(full);
|
||||
if (stat.size < 1024) {
|
||||
fs.rmSync(full);
|
||||
bad++;
|
||||
continue;
|
||||
}
|
||||
const buf = fs.readFileSync(full).subarray(0, 16);
|
||||
const ext = detectExt(buf);
|
||||
if (!ext) {
|
||||
bad++;
|
||||
console.log(`✗ ${id} 非有效图片,跳过`);
|
||||
continue;
|
||||
}
|
||||
const desired = `${id}.${ext}`;
|
||||
if (match !== desired) {
|
||||
fs.renameSync(full, path.join(PUBLIC_DIR, desired));
|
||||
// 清理同 id 其它残留
|
||||
files
|
||||
.filter((f) => f.startsWith(id) && f !== desired && f !== match)
|
||||
.forEach((f) => fs.rmSync(path.join(PUBLIC_DIR, f), { force: true }));
|
||||
}
|
||||
await pool.query("UPDATE artifacts SET image_url = $1 WHERE id = $2", [
|
||||
`/artifacts/${desired}`,
|
||||
id,
|
||||
]);
|
||||
ok++;
|
||||
console.log(`✓ ${id} -> /artifacts/${desired}`);
|
||||
}
|
||||
|
||||
await pool.end();
|
||||
console.log(`校对完成:本地化 ${ok},无效 ${bad}`);
|
||||
};
|
||||
|
||||
main().catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user