129 lines
4.2 KiB
JavaScript
129 lines
4.2 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* Sync sweets videos: ftp-images/bilder-bp/sweets/{dish}/bild/*.mp4 → public/videos/{dishId}.mp4
|
||
* Then compress to uniform 640×480, ≤ 200 KB for modal playback.
|
||
*
|
||
* Usage:
|
||
* node scripts/sync-sweets-videos.mjs
|
||
* node scripts/sync-sweets-videos.mjs --dry-run
|
||
* node scripts/sync-sweets-videos.mjs --skip-optimize
|
||
*/
|
||
import fs from 'fs';
|
||
import path from 'path';
|
||
import { fileURLToPath } from 'url';
|
||
import { spawnSync } from 'child_process';
|
||
|
||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||
const ROOT = path.join(__dirname, '..');
|
||
const SOURCE_DIR = path.join(ROOT, 'ftp-images/bilder-bp/sweets');
|
||
const OUTPUT_DIR = path.join(ROOT, 'public/videos');
|
||
const REPORT_PATH = path.join(__dirname, 'sweets-videos-sync-report.json');
|
||
|
||
const DRY_RUN = process.argv.includes('--dry-run');
|
||
const SKIP_OPTIMIZE = process.argv.includes('--skip-optimize');
|
||
|
||
const FOLDER_TO_DISH = {
|
||
'badam-barfi': 'badam-barfi',
|
||
'besan-patisa': 'baisan-patisa',
|
||
'cham-cham': 'cham-cham',
|
||
'chochlate-barfi': 'chocolate-barfi',
|
||
'coconut-barfi': 'coconut-barfi',
|
||
'gajar-barfi': 'gajar-barfi',
|
||
'gulab-jaman': 'gulab-jaman',
|
||
'habshi-halwa': 'habshi-halwa',
|
||
'jalebi': 'jalebi',
|
||
'laddu': 'laddu',
|
||
'milk-cake-akhrot': 'milk-cake-akhrot',
|
||
'milk-cake-plain': 'milk-cake-plain',
|
||
'namak-paray': 'namakpare',
|
||
'paira': 'paira',
|
||
'patisa': 'patisa',
|
||
'pink-barfi': 'pink-barfi',
|
||
'pistacho-barfi': 'pistachio-barfi',
|
||
'qalakand': 'qalakand',
|
||
'ras-gulay': 'ras-gulay',
|
||
'ras-malai': 'rasmalai',
|
||
'shakar-paray': 'shakar-paray',
|
||
'cream-gulab-jaman': 'cream-gulab-jaman',
|
||
'lambay-gulab-jaman': 'lambay-gulab-jaman',
|
||
'milk-cake-khajoor': 'milk-cake-khajoor',
|
||
'plain-barfi': 'plain-barfi',
|
||
'baisan-barfi': 'baisan-barfi',
|
||
'besan-barfi': 'baisan-barfi',
|
||
'basen-barfi': 'baisan-barfi',
|
||
};
|
||
|
||
const SKIP_FOLDERS = new Set(['samosa-aloo', 'samosa-keema', 'samosa-chaat']);
|
||
|
||
function normalize(value) {
|
||
return String(value || '')
|
||
.toLowerCase()
|
||
.replace(/[^a-z0-9]+/g, '-')
|
||
.replace(/^-+|-+$/g, '');
|
||
}
|
||
|
||
function pickVideo(files) {
|
||
const mp4s = files.filter((f) => /\.mp4$/i.test(f));
|
||
if (!mp4s.length) return null;
|
||
return mp4s.sort((a, b) => a.localeCompare(b))[0];
|
||
}
|
||
|
||
function runOptimize(fileName) {
|
||
const r = spawnSync(process.execPath, [path.join(__dirname, 'optimize-sweets-videos.mjs'), fileName], {
|
||
stdio: 'inherit',
|
||
cwd: ROOT,
|
||
});
|
||
return r.status === 0;
|
||
}
|
||
|
||
function main() {
|
||
console.log('Shahi Kitchen — sweets video sync\n');
|
||
if (!fs.existsSync(SOURCE_DIR)) throw new Error(`Missing: ${SOURCE_DIR}`);
|
||
if (!DRY_RUN) fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||
|
||
const synced = [];
|
||
|
||
for (const folder of fs.readdirSync(SOURCE_DIR, { withFileTypes: true })) {
|
||
if (!folder.isDirectory()) continue;
|
||
const folderNorm = normalize(folder.name);
|
||
if (SKIP_FOLDERS.has(folderNorm)) continue;
|
||
|
||
const dishId = FOLDER_TO_DISH[folderNorm];
|
||
if (!dishId) {
|
||
console.log(`⚠ Unmapped folder: ${folder.name}`);
|
||
continue;
|
||
}
|
||
|
||
const mediaDir = ['bild', 'bilder']
|
||
.map((sub) => path.join(SOURCE_DIR, folder.name, sub))
|
||
.find((d) => fs.existsSync(d));
|
||
if (!mediaDir) continue;
|
||
|
||
const videoFile = pickVideo(fs.readdirSync(mediaDir));
|
||
if (!videoFile) continue;
|
||
|
||
const src = path.join(mediaDir, videoFile);
|
||
const outputFile = `${dishId}.mp4`;
|
||
const dest = path.join(OUTPUT_DIR, outputFile);
|
||
|
||
if (!DRY_RUN) {
|
||
fs.copyFileSync(src, dest);
|
||
if (!SKIP_OPTIMIZE) {
|
||
console.log(` optimizing ${outputFile}…`);
|
||
runOptimize(outputFile);
|
||
}
|
||
}
|
||
|
||
const kb = (DRY_RUN ? fs.statSync(src).size : fs.statSync(dest).size) / 1024;
|
||
synced.push({ dishId, folder: folder.name, sourceFile: videoFile, outputFile, kb: Number(kb.toFixed(1)) });
|
||
const sub = path.basename(mediaDir);
|
||
console.log(`✓ ${dishId} ← ${folder.name}/${sub}/${videoFile} (${kb.toFixed(0)} KB)`);
|
||
}
|
||
|
||
const report = { generatedAt: new Date().toISOString(), dryRun: DRY_RUN, synced };
|
||
if (!DRY_RUN) fs.writeFileSync(REPORT_PATH, JSON.stringify(report, null, 2));
|
||
console.log(`\nSynced: ${synced.length} videos`);
|
||
if (!DRY_RUN) console.log(`Report: ${REPORT_PATH}`);
|
||
}
|
||
|
||
main(); |