|
|
#!/usr/bin/env node |
|
|
|
|
|
import fs from 'fs/promises'; |
|
|
import path from 'path'; |
|
|
import { fileURLToPath } from 'url'; |
|
|
import https from 'https'; |
|
|
import { createWriteStream } from 'fs'; |
|
|
import cliProgress from 'cli-progress'; |
|
|
import chalk from 'chalk'; |
|
|
|
|
|
const __filename = fileURLToPath(import.meta.url); |
|
|
const __dirname = path.dirname(__filename); |
|
|
|
|
|
|
|
|
const INPUT_FILE = path.join(__dirname, 'input', 'font-index.json'); |
|
|
const OUTPUT_DIR = path.join(__dirname, 'output', 'fonts'); |
|
|
|
|
|
|
|
|
const progressBar = new cliProgress.SingleBar({ |
|
|
format: chalk.cyan('{bar}') + ' | {percentage}% | {value}/{total} | {fontName}', |
|
|
barCompleteChar: '\u2588', |
|
|
barIncompleteChar: '\u2591', |
|
|
hideCursor: true |
|
|
}); |
|
|
|
|
|
|
|
|
async function ensureOutputDir() { |
|
|
try { |
|
|
await fs.access(OUTPUT_DIR); |
|
|
} catch { |
|
|
await fs.mkdir(OUTPUT_DIR, { recursive: true }); |
|
|
console.log(chalk.green(`📁 Directory created: ${OUTPUT_DIR}`)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function downloadFile(url, filePath) { |
|
|
return new Promise((resolve, reject) => { |
|
|
const file = createWriteStream(filePath); |
|
|
|
|
|
https.get(url, (response) => { |
|
|
if (response.statusCode === 200) { |
|
|
response.pipe(file); |
|
|
file.on('finish', () => { |
|
|
file.close(); |
|
|
resolve(); |
|
|
}); |
|
|
} else { |
|
|
file.close(); |
|
|
fs.unlink(filePath).catch(() => {}); |
|
|
reject(new Error(`HTTP Error ${response.statusCode} for ${url}`)); |
|
|
} |
|
|
}).on('error', (err) => { |
|
|
file.close(); |
|
|
fs.unlink(filePath).catch(() => {}); |
|
|
reject(err); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
async function downloadFontFamily(fontId, fontData, currentIndex, totalFamilies) { |
|
|
const fontDir = path.join(OUTPUT_DIR, fontId); |
|
|
|
|
|
|
|
|
try { |
|
|
await fs.access(fontDir); |
|
|
} catch { |
|
|
await fs.mkdir(fontDir, { recursive: true }); |
|
|
} |
|
|
|
|
|
let downloadedCount = 0; |
|
|
const totalFiles = countTotalFiles(fontData); |
|
|
const allFiles = getAllFiles(fontData, fontId); |
|
|
|
|
|
|
|
|
progressBar.start(totalFiles, 0, { fontName: fontData.family }); |
|
|
|
|
|
|
|
|
for (const fileInfo of allFiles) { |
|
|
const { fileName, filePath, url } = fileInfo; |
|
|
|
|
|
try { |
|
|
|
|
|
try { |
|
|
await fs.access(filePath); |
|
|
downloadedCount++; |
|
|
progressBar.update(downloadedCount, { fontName: fontData.family }); |
|
|
continue; |
|
|
} catch { |
|
|
|
|
|
} |
|
|
|
|
|
await downloadFile(url, filePath); |
|
|
downloadedCount++; |
|
|
progressBar.update(downloadedCount, { fontName: fontData.family }); |
|
|
} catch (error) { |
|
|
|
|
|
console.log(chalk.red(`\n❌ Error for ${fileName}: ${error.message}`)); |
|
|
} |
|
|
} |
|
|
|
|
|
progressBar.stop(); |
|
|
return downloadedCount; |
|
|
} |
|
|
|
|
|
|
|
|
function countTotalFiles(fontData) { |
|
|
let count = 0; |
|
|
for (const weightData of Object.values(fontData.variants)) { |
|
|
for (const styleData of Object.values(weightData)) { |
|
|
for (const subsetData of Object.values(styleData)) { |
|
|
count += Object.keys(subsetData.url).length; |
|
|
} |
|
|
} |
|
|
} |
|
|
return count; |
|
|
} |
|
|
|
|
|
|
|
|
function getAllFiles(fontData, fontId) { |
|
|
const files = []; |
|
|
const fontDir = path.join(OUTPUT_DIR, fontId); |
|
|
|
|
|
|
|
|
const mainWeight = fontData.variants['400']; |
|
|
if (!mainWeight) { |
|
|
|
|
|
const firstWeight = Object.keys(fontData.variants)[0]; |
|
|
const firstWeightData = fontData.variants[firstWeight]; |
|
|
const normalStyle = firstWeightData['normal']; |
|
|
|
|
|
if (normalStyle) { |
|
|
|
|
|
const defaultSubset = fontData.defSubset || Object.keys(normalStyle)[0]; |
|
|
const subsetData = normalStyle[defaultSubset]; |
|
|
|
|
|
if (subsetData) { |
|
|
for (const [format, url] of Object.entries(subsetData.url)) { |
|
|
const fileName = `${fontId}-${firstWeight}-normal-${defaultSubset}.${format}`; |
|
|
const filePath = path.join(fontDir, fileName); |
|
|
files.push({ fileName, filePath, url }); |
|
|
} |
|
|
} |
|
|
} |
|
|
return files; |
|
|
} |
|
|
|
|
|
const normalStyle = mainWeight['normal']; |
|
|
if (!normalStyle) { |
|
|
|
|
|
const firstStyle = Object.keys(mainWeight)[0]; |
|
|
const firstStyleData = mainWeight[firstStyle]; |
|
|
const defaultSubset = fontData.defSubset || Object.keys(firstStyleData)[0]; |
|
|
const subsetData = firstStyleData[defaultSubset]; |
|
|
|
|
|
if (subsetData) { |
|
|
for (const [format, url] of Object.entries(subsetData.url)) { |
|
|
const fileName = `${fontId}-400-${firstStyle}-${defaultSubset}.${format}`; |
|
|
const filePath = path.join(fontDir, fileName); |
|
|
files.push({ fileName, filePath, url }); |
|
|
} |
|
|
} |
|
|
return files; |
|
|
} |
|
|
|
|
|
|
|
|
const defaultSubset = fontData.defSubset || Object.keys(normalStyle)[0]; |
|
|
const subsetData = normalStyle[defaultSubset]; |
|
|
|
|
|
if (subsetData) { |
|
|
for (const [format, url] of Object.entries(subsetData.url)) { |
|
|
const fileName = `${fontId}-400-normal-${defaultSubset}.${format}`; |
|
|
const filePath = path.join(fontDir, fileName); |
|
|
files.push({ fileName, filePath, url }); |
|
|
} |
|
|
} |
|
|
|
|
|
return files; |
|
|
} |
|
|
|
|
|
|
|
|
async function main() { |
|
|
try { |
|
|
console.log(chalk.blue.bold('🚀 Starting download of main fonts...\n')); |
|
|
|
|
|
|
|
|
try { |
|
|
await fs.access(INPUT_FILE); |
|
|
} catch { |
|
|
throw new Error(`Input file not found: ${INPUT_FILE}`); |
|
|
} |
|
|
|
|
|
|
|
|
await ensureOutputDir(); |
|
|
|
|
|
|
|
|
console.log(chalk.yellow('📖 Reading font-index.json file...')); |
|
|
const fontIndexData = JSON.parse(await fs.readFile(INPUT_FILE, 'utf8')); |
|
|
|
|
|
const fontIds = Object.keys(fontIndexData); |
|
|
console.log(chalk.cyan(`📊 ${fontIds.length} font families found`)); |
|
|
console.log(chalk.yellow('📝 Mode: Download main variants only (weight 400, normal style)\n')); |
|
|
|
|
|
let totalDownloaded = 0; |
|
|
let processedFamilies = 0; |
|
|
|
|
|
|
|
|
for (let i = 0; i < fontIds.length; i++) { |
|
|
const fontId = fontIds[i]; |
|
|
const fontData = fontIndexData[fontId]; |
|
|
|
|
|
console.log(chalk.magenta(`\n🔤 [${i + 1}/${fontIds.length}] Traitement de "${fontData.family}" (${fontId})`)); |
|
|
|
|
|
const downloaded = await downloadFontFamily(fontId, fontData, i, fontIds.length); |
|
|
totalDownloaded += downloaded; |
|
|
processedFamilies++; |
|
|
|
|
|
|
|
|
const progress = ((processedFamilies / fontIds.length) * 100).toFixed(1); |
|
|
console.log(chalk.green(`✅ ${downloaded} fichiers téléchargés pour "${fontData.family}"`)); |
|
|
console.log(chalk.blue(`📈 Progrès global : ${processedFamilies}/${fontIds.length} familles (${progress}%)`)); |
|
|
console.log(chalk.blue(`📊 Total téléchargé : ${totalDownloaded} fichiers\n`)); |
|
|
} |
|
|
|
|
|
console.log(chalk.green.bold('🎉 Téléchargement terminé !')); |
|
|
console.log(chalk.cyan('📊 Summary :')); |
|
|
console.log(chalk.white(` - ${processedFamilies} familles de polices traitées`)); |
|
|
console.log(chalk.white(` - ${totalDownloaded} fichiers téléchargés`)); |
|
|
console.log(chalk.white(` - Dossier de sortie : ${OUTPUT_DIR}`)); |
|
|
|
|
|
} catch (error) { |
|
|
console.error(chalk.red('❌ Erreur :'), error.message); |
|
|
process.exit(1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
main(); |
|
|
|