Download scripts/update-emoji.js from kavion/quote-api: direct link, hf CLI and curl.
- Browser
- Download file 4.97 kB
-
https://huggingface.co/spaces/kavion/quote-api/resolve/main/scripts/update-emoji.js
- Command line
-
hf download hf://spaces/kavion/quote-api/scripts/update-emoji.js
-
curl -L -o update-emoji.js https://huggingface.co/spaces/kavion/quote-api/resolve/main/scripts/update-emoji.js
4.97 kB
| /** | |
| * Downloads emoji images from emoji-datasource CDN (jsdelivr) and saves them | |
| * as base64 JSON files for each brand. | |
| * | |
| * Supported brands via emoji-datasource: apple, google, twitter | |
| * Unsupported (kept as-is): joypixels, blob | |
| * | |
| * Usage: | |
| * node scripts/update-emoji.js # update all supported brands | |
| * node scripts/update-emoji.js apple # update only apple | |
| * node scripts/update-emoji.js google twitter # update specific brands | |
| */ | |
| const path = require('path') | |
| const fs = require('fs') | |
| const https = require('https') | |
| const EmojiDbLib = require('emoji-db') | |
| const emojiDb = new EmojiDbLib({ useDefaultDb: true }) | |
| const EMOJI_DIR = path.resolve(__dirname, '../assets/emoji/') | |
| const CDN_VERSION = '16.0.0' | |
| const CDN_SIZE = 64 // px | |
| const CONCURRENCY = 50 | |
| const SUPPORTED_BRANDS = { | |
| apple: { | |
| pkg: 'emoji-datasource-apple', | |
| folder: 'apple' | |
| }, | |
| google: { | |
| pkg: 'emoji-datasource-google', | |
| folder: 'google' | |
| }, | |
| twitter: { | |
| pkg: 'emoji-datasource-twitter', | |
| folder: 'twitter' | |
| } | |
| } | |
| const BRAND_JSON_FILES = { | |
| apple: 'emoji-apple-image.json', | |
| google: 'emoji-google-image.json', | |
| twitter: 'emoji-twitter-image.json', | |
| joypixels: 'emoji-joypixels-image.json', | |
| blob: 'emoji-blob-image.json' | |
| } | |
| function fetchBuffer (url) { | |
| return new Promise((resolve, reject) => { | |
| const req = https.get(url, (res) => { | |
| if (res.statusCode !== 200) { | |
| res.resume() | |
| return reject(new Error(`HTTP ${res.statusCode} for ${url}`)) | |
| } | |
| const chunks = [] | |
| res.on('data', (chunk) => chunks.push(chunk)) | |
| res.on('end', () => resolve(Buffer.concat(chunks))) | |
| res.on('error', reject) | |
| }) | |
| req.on('error', reject) | |
| req.setTimeout(10000, () => { | |
| req.destroy() | |
| reject(new Error(`Timeout for ${url}`)) | |
| }) | |
| }) | |
| } | |
| function getCdnUrl (brand, emojiCode) { | |
| const config = SUPPORTED_BRANDS[brand] | |
| return `https://cdn.jsdelivr.net/npm/${config.pkg}@${CDN_VERSION}/img/${config.folder}/${CDN_SIZE}/${emojiCode}.png` | |
| } | |
| async function runPool (tasks, concurrency) { | |
| const results = [] | |
| let index = 0 | |
| async function worker () { | |
| while (index < tasks.length) { | |
| const i = index++ | |
| results[i] = await tasks[i]().catch(() => null) | |
| } | |
| } | |
| const workers = Array.from({ length: Math.min(concurrency, tasks.length) }, () => worker()) | |
| await Promise.all(workers) | |
| return results | |
| } | |
| async function updateBrand (brand) { | |
| const config = SUPPORTED_BRANDS[brand] | |
| if (!config) { | |
| console.log(` Skipping ${brand} — not supported by emoji-datasource (keeping existing JSON)`) | |
| return | |
| } | |
| const jsonFile = path.join(EMOJI_DIR, BRAND_JSON_FILES[brand]) | |
| // Load existing data | |
| let existing = {} | |
| try { | |
| if (fs.existsSync(jsonFile)) { | |
| existing = JSON.parse(fs.readFileSync(jsonFile, 'utf8')) | |
| } | |
| } catch (err) { | |
| console.warn(` Warning: could not read existing ${jsonFile}: ${err.message}`) | |
| } | |
| const dbData = emojiDb.dbData | |
| const codes = Object.keys(dbData).filter(k => !dbData[k].qualified) | |
| // Find missing emoji codes | |
| const missing = codes.filter(code => !existing[code]) | |
| const total = codes.length | |
| const alreadyHave = total - missing.length | |
| console.log(` ${brand}: ${total} emoji total, ${alreadyHave} cached, ${missing.length} to download`) | |
| if (missing.length === 0) { | |
| console.log(` ${brand}: up to date`) | |
| return | |
| } | |
| let downloaded = 0 | |
| let failed = 0 | |
| const tasks = missing.map(code => async () => { | |
| try { | |
| const url = getCdnUrl(brand, code) | |
| const buffer = await fetchBuffer(url) | |
| const base64 = buffer.toString('base64') | |
| if (base64.length > 0) { | |
| downloaded++ | |
| if (downloaded % 100 === 0) process.stdout.write(` ${brand}: ${downloaded}/${missing.length}\r`) | |
| return { code, base64 } | |
| } | |
| } catch (err) { | |
| failed++ | |
| return null | |
| } | |
| }) | |
| const results = await runPool(tasks, CONCURRENCY) | |
| for (const result of results) { | |
| if (result) existing[result.code] = result.base64 | |
| } | |
| console.log(` ${brand}: downloaded ${downloaded}, failed ${failed}, total ${Object.keys(existing).length}`) | |
| // Write atomically | |
| const tmpFile = jsonFile + '.tmp' | |
| fs.writeFileSync(tmpFile, JSON.stringify(existing)) | |
| fs.renameSync(tmpFile, jsonFile) | |
| console.log(` ${brand}: saved to ${path.basename(jsonFile)}`) | |
| } | |
| async function main () { | |
| const args = process.argv.slice(2) | |
| const brands = args.length > 0 ? args : Object.keys(BRAND_JSON_FILES) | |
| console.log('Emoji updater — CDN: emoji-datasource@' + CDN_VERSION + ' via jsdelivr') | |
| console.log('Brands to update:', brands.join(', ')) | |
| console.log() | |
| for (const brand of brands) { | |
| if (!BRAND_JSON_FILES[brand]) { | |
| console.error(`Unknown brand: ${brand}`) | |
| continue | |
| } | |
| await updateBrand(brand) | |
| } | |
| console.log('\nDone.') | |
| } | |
| main().catch(err => { | |
| console.error('Fatal error:', err) | |
| process.exit(1) | |
| }) | |