dyno-bot/color-service-master/dynoav.js
2020-09-12 19:08:48 +01:00

131 lines
3.0 KiB
JavaScript

'use strict';
const fs = require('fs');
const path = require('path');
const axios = require('axios');
const sharp = require('sharp');
const express = require('express');
const router = express.Router();
const file = path.join(__dirname, 'dyno.svg');
const gifRegexp = /^GIF8[79]a/;
const pngSignature = 'PNG\r\n\x1a\n';
const pngImageHeaderChunkName = 'IHDR';
const pngFriedChunkName = 'CgBI';
let svg;
function getOrLoadSvg(file) {
if (svg) {
return Promise.resolve(svg);
}
return new Promise((res, rej) => {
fs.readFile(file, (err, data) => {
if (err) {
return rej(err);
}
svg = data.toString('utf-8');
return res(svg);
});
});
}
async function getSvg(c, w, h) {
let svg = await getOrLoadSvg(file);
let vars = { c, w, h };
let s = svg;
for (let [key, val] of Object.entries(vars)) {
s = s.replace(`{{${key}}}`, val)
}
return s;
}
function isGif(buffer) {
var signature = buffer.toString('ascii', 0, 6);
return (gifRegexp.test(signature));
}
function isPng(buffer) {
if (pngSignature === buffer.toString('ascii', 1, 8)) {
var chunkName = buffer.toString('ascii', 12, 16);
if (chunkName === pngFriedChunkName) {
chunkName = buffer.toString('ascii', 28, 32);
}
if (chunkName !== pngImageHeaderChunkName) {
throw new TypeError('invalid png');
}
return true;
}
}
function getSize(buffer) {
if (isGif(buffer)) {
return {
width: buffer.readUInt16LE(6),
height: buffer.readUInt16LE(8)
};
} else if (isPng(buffer)) {
if (buffer.toString('ascii', 12, 16) === pngFriedChunkName) {
return {
width: buffer.readUInt32BE(32),
height: buffer.readUInt32BE(36)
};
}
return {
width: buffer.readUInt32BE(16),
height: buffer.readUInt32BE(20)
};
}
}
router.get('/', async (req, res) => {
if (!req.query.url) {
return res.status(400).send('Missing URL query.');
}
const color = 'FFFFFF';
try {
let av = req.query.url.replace(/\?(size\=[0-9]+).*/, '');
av += `?size=256`;
const reqSize = req.query.size || '256x256';
let [width, height] = reqSize.split('x');
width = parseInt(width, 10) > 2048 ? 2048 : parseInt(width, 10);
if (!height) {
height = width;
}
height = parseInt(height, 10) > 2048 ? 2048 : parseInt(height, 10);
if (width > height) {
width = height;
} else if (height > width) {
height = width;
}
const avatar = await axios.get(av, {
headers: { Accept: 'image/*' },
responseType: 'arraybuffer',
});
const size = getSize(avatar.data);
if (size && size.width <= width) {
width = size.width;
height = size.height;
}
const svg = await getSvg(color, width, height);
const img = await sharp(avatar.data).overlayWith(Buffer.from(svg), { cutout: true }).png().toBuffer();
res.setHeader('Content-Type', 'image/png');
res.end(img);
} catch (err) {
console.error(err);
throw err;
}
});
module.exports = router;