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
This commit is contained in:
74
backend/scripts/sync-media.mjs
Normal file
74
backend/scripts/sync-media.mjs
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 媒体同步脚本:将 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);
|
||||
});
|
||||
Reference in New Issue
Block a user