Files
fitness-coach/backend/scripts/sync-media.mjs
wm b3b58e66fb feat: init fitness-coach (backend + miniprogram)
- backend: NestJS service with exercise/plan data
- miniprogram: WeChat mini program client
- exclude node_modules, dist, runtime data-store, local env
2026-07-22 00:35:40 +08:00

75 lines
2.6 KiB
JavaScript
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.

/**
* 媒体同步脚本:将 exercises-dataset 的 images/ 与 videos/ 下载到 backend/media/
* 使后端 /media 静态服务可直接提供缩略图与动画 GIF。
*
* 用法: node scripts/sync-media.mjs
* 可选环境变量:
* MEDIA_CONCURRENCY=8 并发下载数
* MEDIA_ONLY=gif|image 仅下载某类gif / image
*/
import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const DATA_FILE = path.join(__dirname, '..', 'src', 'exercises', 'data', 'exercises.json');
const MEDIA_DIR = path.join(__dirname, '..', 'media');
const RAW_BASE = 'https://raw.githubusercontent.com/hasaneyldrm/exercises-dataset/main/';
const CONCURRENCY = parseInt(process.env.MEDIA_CONCURRENCY || '8', 10);
const ONLY = process.env.MEDIA_ONLY || 'all';
async function main() {
if (!fs.existsSync(DATA_FILE)) {
console.error('[sync-media] 未找到数据集,请先运行 npm run build 或手动放置 exercises.json');
process.exit(1);
}
const raw = JSON.parse(fs.readFileSync(DATA_FILE, 'utf-8'));
const paths = new Set();
for (const e of raw) {
if (e.image && (ONLY === 'all' || ONLY === 'image')) paths.add(e.image);
if (e.gif_url && (ONLY === 'all' || ONLY === 'gif')) paths.add(e.gif_url);
}
fs.mkdirSync(MEDIA_DIR, { recursive: true });
const list = Array.from(paths);
console.log(`[sync-media] 待下载媒体 ${list.length} 个,并发 ${CONCURRENCY}`);
let done = 0;
let skipped = 0;
let failed = 0;
const queue = [...list];
async function worker() {
while (queue.length) {
const p = queue.shift();
const dest = path.join(MEDIA_DIR, p);
if (fs.existsSync(dest) && fs.statSync(dest).size > 0) {
skipped++;
continue;
}
try {
const res = await fetch(RAW_BASE + p);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const buf = Buffer.from(await res.arrayBuffer());
fs.mkdirSync(path.dirname(dest), { recursive: true });
fs.writeFileSync(dest, buf);
done++;
} catch (err) {
failed++;
console.error(` 失败 ${p}: ${err.message}`);
}
if ((done + skipped + failed) % 50 === 0) {
console.log(` 进度 ${done + skipped + failed}/${list.length}`);
}
}
}
await Promise.all(Array.from({ length: CONCURRENCY }, () => worker()));
console.log(`[sync-media] 完成:新增 ${done},已存在 ${skipped},失败 ${failed}`);
}
main().catch((e) => {
console.error('[sync-media] 错误:', e.message);
process.exit(1);
});