This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
Zaimki/server/routes/banner.js

119 lines
4.8 KiB
JavaScript
Raw Normal View History

2020-10-31 13:33:59 -07:00
import { Router } from 'express';
import SQL from 'sql-template-strings';
import {createCanvas, loadImage, registerFont} from "canvas";
2020-11-10 14:41:56 -08:00
import { loadSuml } from '../loader';
import avatar from '../avatar';
import {buildPronoun, parsePronouns} from "../../src/buildPronoun";
2020-10-31 13:33:59 -07:00
import {loadTsv} from "../../src/tsv";
import {handleErrorAsync} from "../../src/helpers";
2021-06-17 13:54:08 -07:00
import fs from 'fs';
2020-10-16 14:32:51 -07:00
2020-11-10 14:41:56 -08:00
const translations = loadSuml('translations');
2020-10-16 14:32:51 -07:00
const drawCircle = (context, image, x, y, size) => {
context.save();
context.beginPath();
context.arc(x + size / 2, y + size / 2, size / 2, 0, Math.PI * 2, true);
context.closePath();
context.clip();
context.drawImage(image, x, y, size, size)
context.beginPath();
context.arc(x, y, size / 2, 0, Math.PI * 2, true);
context.clip();
context.closePath();
context.restore();
}
2020-07-26 03:59:49 -07:00
2020-10-31 13:33:59 -07:00
const router = Router();
2020-07-22 13:19:23 -07:00
router.get('/banner/:pronounName*.png', handleErrorAsync(async (req, res) => {
2020-07-22 13:19:23 -07:00
const width = 1200
const height = 600
const mime = 'image/png';
const imageSize = 200;
2020-10-16 14:32:51 -07:00
let leftRatio = 4;
2020-07-22 13:19:23 -07:00
2021-06-17 13:54:08 -07:00
const pronounName = req.params.pronounName + req.params[0];
const cacheDir = `${__dirname}/../../cache/banner`;
fs.mkdirSync(cacheDir, { recursive: true });
const cacheFilename = `${cacheDir}/${pronounName}.png`;
if (fs.existsSync(cacheFilename) && fs.statSync(cacheFilename).mtimeMs >= (new Date() - 24*60*60*1000)) {
return res.set('content-type', mime).send(fs.readFileSync(cacheFilename));
}
const saveToCacheAndReturn = (canvas) => {
const buffer = canvas.toBuffer(mime);
fs.writeFileSync(cacheFilename, buffer);
return res.set('content-type', mime).send(buffer);
}
2020-07-22 13:19:23 -07:00
registerFont('static/fonts/quicksand-v21-latin-ext_latin-regular.ttf', { family: 'Quicksand', weight: 'regular'});
registerFont('static/fonts/quicksand-v21-latin-ext_latin-700.ttf', { family: 'Quicksand', weight: 'bold'});
const canvas = createCanvas(width, height)
const context = canvas.getContext('2d')
2020-12-07 10:54:39 -08:00
const bg = await loadImage('static/bg.png');
context.drawImage(bg, 0, 0, width, height);
2020-07-22 13:19:23 -07:00
2020-10-16 14:32:51 -07:00
const fallback = async _ => {
const logo = await loadImage('node_modules/@fortawesome/fontawesome-pro/svgs/light/tags.svg');
leftRatio = 5;
context.drawImage(logo, width / leftRatio - imageSize / 2, height / 2 - imageSize / 1.25 / 2, imageSize, imageSize / 1.25);
context.font = `regular ${translations.title.length < 10 ? 120 : translations.title.length < 14 ? 80 : 72}pt Quicksand`;
context.fillText(translations.title, width / leftRatio + imageSize / 1.5, height / 2 + (translations.title.length < 10 ? 48 : translations.title.length < 14 ? 24 : 24));
2020-10-16 14:32:51 -07:00
}
if (pronounName.startsWith('@')) {
const user = await req.db.get(SQL`SELECT id, username, email, avatarSource FROM users WHERE username=${pronounName.substring(1)}`);
2020-10-16 14:32:51 -07:00
if (!user) {
await fallback();
2021-06-17 13:54:08 -07:00
return saveToCacheAndReturn(canvas);
2020-10-16 14:32:51 -07:00
}
const avatarImage = await loadImage(await avatar(req.db, user));
2020-07-22 13:19:23 -07:00
drawCircle(context, avatarImage, width / leftRatio - imageSize / 2, height / 2 - imageSize / 2, imageSize);
2020-07-22 13:19:23 -07:00
2020-10-16 14:32:51 -07:00
context.font = `regular 48pt Quicksand`
context.fillText('@' + user.username, width / leftRatio + imageSize, height / 2)
const logo = await loadImage('static/favicon.svg');
context.font = 'regular 24pt Quicksand'
context.fillStyle = '#C71585';
const logoSize = 24 * 1.25;
context.drawImage(logo, width / leftRatio + imageSize, height / 2 + logoSize - 4, logoSize, logoSize / 1.25)
context.fillText(translations.title, width / leftRatio + imageSize + 36, height / 2 + 48);
2021-06-17 13:54:08 -07:00
return saveToCacheAndReturn(canvas);
2020-10-16 14:32:51 -07:00
}
const pronoun = buildPronoun(
parsePronouns(loadTsv(__dirname + '/../../data/pronouns/pronouns.tsv')),
pronounName,
2020-10-16 14:32:51 -07:00
);
const logo = await loadImage('node_modules/@fortawesome/fontawesome-pro/svgs/light/tags.svg');
2020-11-15 07:10:03 -08:00
if (!pronoun && pronounName !== req.config.pronouns.any) {
2020-10-16 14:32:51 -07:00
await fallback();
2021-06-17 13:54:08 -07:00
return saveToCacheAndReturn(canvas);
2020-07-22 13:19:23 -07:00
}
2020-10-16 14:32:51 -07:00
context.drawImage(logo, width / leftRatio - imageSize / 2, height / 2 - imageSize / 1.25 / 2, imageSize, imageSize / 1.25)
context.font = 'regular 48pt Quicksand'
context.fillText(translations.pronouns.intro + ':', width / leftRatio + imageSize / 1.5, height / 2 - 36)
2020-10-16 14:32:51 -07:00
2020-12-10 07:23:39 -08:00
const pronounNameOptions = pronounName === req.config.pronouns.any ? [req.config.pronouns.any] : pronoun.nameOptions();
context.font = `bold ${pronounNameOptions.length <= 2 ? '70' : '36'}pt Quicksand`
2021-06-17 13:54:08 -07:00
context.fillText(pronounNameOptions.join('\n'), width / leftRatio + imageSize / 1.5, height / 2 + (pronounNameOptions.length <= 2 ? 72 : 24));
2020-10-16 14:32:51 -07:00
2021-06-17 13:54:08 -07:00
return saveToCacheAndReturn(canvas);
}));
2020-10-31 13:33:59 -07:00
export default router;