From 779289865373a18e13c555b97d9dce018c2f1ac7 Mon Sep 17 00:00:00 2001 From: split Date: Fri, 12 Jul 2024 01:49:22 -0700 Subject: [PATCH] 1.2: add multi-format --- .dockerignore | 1 + .env.example | 10 +- Dockerfile | 2 +- static/default/1024.png => assets/default.png | Bin bun.lockb | Bin 71140 -> 70704 bytes package.json | 2 +- src/app.d.ts | 3 + src/lib/avatars.ts | 172 +++++++++++++++--- src/lib/configuration.ts | 20 +- src/routes/+layout.svelte | 13 +- .../avatar/[identifier]/[[size]]/+server.ts | 10 +- src/routes/set/+page.server.ts | 16 +- static/default/128.png | Bin 4204 -> 0 bytes static/default/256.png | Bin 8189 -> 0 bytes static/default/32.png | Bin 1111 -> 0 bytes static/default/512.png | Bin 14625 -> 0 bytes static/default/64.png | Bin 2055 -> 0 bytes static/default/index.png | Bin 14625 -> 0 bytes vite.config.ts | 8 +- 19 files changed, 210 insertions(+), 47 deletions(-) rename static/default/1024.png => assets/default.png (100%) delete mode 100644 static/default/128.png delete mode 100644 static/default/256.png delete mode 100644 static/default/32.png delete mode 100644 static/default/512.png delete mode 100644 static/default/64.png delete mode 100644 static/default/index.png diff --git a/.dockerignore b/.dockerignore index 15187ac..ba09860 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,4 +8,5 @@ README.md .prettierignore .gitignore .env.example +.env .svelte-kit \ No newline at end of file diff --git a/.env.example b/.env.example index 0e06d31..e7fd614 100644 --- a/.env.example +++ b/.env.example @@ -17,8 +17,14 @@ USERINFO__ROUTE= # Identifier you'd like to use to link avatars with USERINFO__IDENTIFIER=preferred_username -# Comma-separated list of allowed image types -ALLOWED_TYPES=image/jpg,image/jpeg,image/png,image/png,image/webp,image/gif +# Comma-separated list of any extra formats you'd like to be available +IMAGES__EXTRA_OUTPUT_FORMATS=jpeg +# Comma-separated list of output resolutions you'd like to be available - Default 1024,512,256,128,64,32 +IMAGES__OUTPUT_RESOLUTIONS=1024,512,256,128,64,48,32 +# Default selected resolution - Default 512 +IMAGES__DEFAULT_RESOLUTION=512 +# Comma-separated list of permitted image mimetypes +IMAGES__ALLOWED_INPUT_FORMATS=image/jpg,image/jpeg,image/png,image/webp,image/gif # Prisma database URL DATABASE_URL=file:../.data/data.db diff --git a/Dockerfile b/Dockerfile index 2699866..afb026f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,7 +34,7 @@ FROM base2 AS release COPY --from=prisma /usr/src/app/prisma prisma COPY --from=prisma /usr/src/app/node_modules node_modules COPY --from=build /usr/src/app/build build -COPY --from=build /usr/src/app/static static +COPY --from=build /usr/src/app/assets assets COPY --from=build /usr/src/app/package.json . EXPOSE 3000/tcp diff --git a/static/default/1024.png b/assets/default.png similarity index 100% rename from static/default/1024.png rename to assets/default.png diff --git a/bun.lockb b/bun.lockb index d93105c778966c878e771394eede812297b834dd..4370d51b17d3500f20b22d0f648e38e46bae3802 100755 GIT binary patch delta 11790 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 delta 12022 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 Q&wcV{Ex-EVN2G=S1NQ+%>;M1& diff --git a/package.json b/package.json index 23a925d..0510e7c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ava", - "version": "1.1.1", + "version": "1.2.0", "private": true, "scripts": { "dev": "vite dev", diff --git a/src/app.d.ts b/src/app.d.ts index 743f07b..a32c3e8 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -8,6 +8,9 @@ declare global { // interface PageState {} // interface Platform {} } + + declare const __APP_NAME__: string; + declare const __APP_VERSION__: string; } export {}; diff --git a/src/lib/avatars.ts b/src/lib/avatars.ts index ca4bb6a..d4bd3e7 100644 --- a/src/lib/avatars.ts +++ b/src/lib/avatars.ts @@ -3,56 +3,167 @@ import { existsSync } from "node:fs" import { join } from "node:path" import { prisma } from "./clientsingleton" import configuration from "./configuration" -import Sharp from "sharp" +import Sharp, { type FormatEnum } from "sharp" import mime from "mime" // todo: make customizable export const avatarDirectory = "./.data/avatars" -export const defaultAvatarDirectory = "./static/default/" -export const renderSizes = [ 1024, 512, 256, 128, 64, 32 ] +export const defaultAvatarDirectory = "./.data/defaultAvatar/" -export async function getPathToAvatarForUid(uid?: string, size: string = "512") { +await mkdir(defaultAvatarDirectory, { recursive: true }) + +export const missingAvatarQueue = new Map< + string, + Promise +>() + +/** + * @description Generate an avatar at the selected size and format + * @param path Path to the avatar directory + * @param size Avatar size + * @param fmt Avatar format + * @returns Promise that resolves to the path of the newly-generated avatar + */ +export function generateMissingAvatar(path: string, size: number, fmt?: keyof Sharp.FormatEnum) { + let qid = JSON.stringify([path, size, fmt]) + if (missingAvatarQueue.has(qid)) + return missingAvatarQueue.get(qid)! + + let prom = new Promise(async (res, rej) => { + // locate best quality currently available + const av = await readdir(path) + // this can probably be done better but I DON'T GIVE A FUCK !!!! + const pathToBestQualityImg = + path == defaultAvatarDirectory + ? "./assets/default.png" + : join( + path, + av + .map(e => [parseInt(e.match(/(.*)\..*/)?.[1] || "", 10), e] as [number, string]) + .sort(([a],[b]) => b - a) + [0][1] + ) + + const buf = await Bun.file(pathToBestQualityImg).arrayBuffer() + res(writeAvatar(path, await renderAvatar(buf, size, fmt))) + missingAvatarQueue.delete(qid) + }) + + missingAvatarQueue.set(qid, prom) + return prom +} + +/** + * @description Get the path of an avatar for a user + * @param uid UID of the user + * @param size Avatar size + * @param fmt Avatar format + * @returns Path to the avatar of a user + */ +export async function getPathToAvatarForUid(uid?: string, size: number = configuration.images.default_resolution, fmt?: string) { if (uid?.includes("/")) throw Error("UID cannot include /") + // check if format is valid + if (![undefined, ...configuration.images.extra_output_types].includes(fmt as keyof FormatEnum)) + return + + // if no uid / no avatar folder then default to the default avatar directory let userAvatarDirectory = uid ? join(avatarDirectory, uid) : defaultAvatarDirectory if (!existsSync(userAvatarDirectory)) userAvatarDirectory = defaultAvatarDirectory - let sizes = await readdir(userAvatarDirectory) - const targetAvatar = sizes.find(s => s.match(/(.*)\..*/)?.[1] == size) + // bind a makeMissing function + const makeMissing = generateMissingAvatar.bind(null, userAvatarDirectory, size, fmt as keyof FormatEnum) + + // get directory to extract imgs from + let targetAvatarDirectory = join(userAvatarDirectory, fmt||"") + + // if there's no images for the specified fmt, generate new ones + if (!existsSync(targetAvatarDirectory)) + return makeMissing() + + let sizes = await readdir(targetAvatarDirectory, {withFileTypes: true}) + + const targetAvatar = sizes.filter(e => e.isFile()).find( + s => parseInt(s.name.match(/(.*)\..*/)?.[1] || "", 10) == size + ) + if (targetAvatar) - return join(userAvatarDirectory, targetAvatar) + return join(targetAvatarDirectory, targetAvatar.name) + else if (configuration.images.output_resolutions.includes(size)) + return makeMissing() // generate image at this size for the specified format } -export async function getPathToAvatarForIdentifier(identifier: string, size: string = "512") { +export async function getPathToAvatarForIdentifier(identifier: string, size: number = configuration.images.default_resolution, fmt?: string) { let user = await prisma.user.findFirst({ where: { identifier } }) - return getPathToAvatarForUid(user?.userId, size) + return getPathToAvatarForUid(user?.userId, size, fmt) } -export async function rerenderAvatar(bin: ArrayBuffer, squareSize: number) { +/** + * @description Render an avatar at the specified size and format + * @param bin Image to rerender + * @param squareSize Target size of the new image + * @param format Image target format + * @returns Avatar buffer and other information which may be useful + */ +export async function renderAvatar(bin: ArrayBuffer|Buffer, squareSize: number, format?: keyof Sharp.FormatEnum) { + const opBegin = Date.now() let img = Sharp(bin); let metadata = await img.metadata(); - squareSize = Math.min(...[metadata.width, metadata.height].filter(e => e) as number[], squareSize) + let realSquareSize = Math.min(...[metadata.width, metadata.height].filter(e => e) as number[], squareSize) img.resize({ - width: squareSize, - height: squareSize, + width: realSquareSize, + height: realSquareSize, fit: "cover" }) - return img.toBuffer() + if (format) img.toFormat(format) + + return { + buf: await img.toBuffer(), + extension: format || metadata.format, + requestedFormat: format, + squareSize, + time: Date.now()-opBegin + } +} + +export async function writeAvatar(avatarDir: string, renderedAvatar: Awaited>) { + const targetDir = join( + avatarDir, + ...( + renderedAvatar.requestedFormat + ? [ renderedAvatar.requestedFormat ] + : [] + ) + ) + + await mkdir(targetDir, {recursive: true}) + + const targetPath = join( + targetDir, + `${renderedAvatar.squareSize}.${renderedAvatar.extension}` + ) + + await Bun.write( + targetPath, + renderedAvatar.buf + ) + + return targetPath } export async function setNewAvatar(uid: string, avatar?: File) { if (uid?.includes("/")) throw Error("UID cannot include /") - + // Delete current avatar directory const userAvatarDirectory = join(avatarDirectory, uid) await rm(userAvatarDirectory, { recursive: true, force: true }) @@ -62,24 +173,25 @@ export async function setNewAvatar(uid: string, avatar?: File) { // make a new directory mkdir(userAvatarDirectory, { recursive: true }) - let time: Record = {} + let time: Record> = {} // render all images and write to disk let avatarData = await avatar.arrayBuffer() - let fileExtension = mime.getExtension(avatar.type) - for (let x of renderSizes) { - console.log(x) - try { - let start = Date.now() - let rerenderedAvatarData = await rerenderAvatar(avatarData, x) - await Bun.write( - join(userAvatarDirectory, `${x}.${fileExtension}`), - rerenderedAvatarData - ) - time[x] = Date.now()-start - } catch (e) { // clear pfp and throw if error encountered - await rm(userAvatarDirectory, { recursive: true, force: true }) - throw e + for (let x of configuration.images.output_resolutions) { + time[x] = Object.fromEntries([ + ["input", -1], + ...configuration.images.extra_output_types + .map( e => [ e, -1 ] ) + ]) + for (let t of [undefined, ...configuration.images.extra_output_types]) { + try { + const rendered = await renderAvatar(avatarData, x, t) + await writeAvatar(userAvatarDirectory, rendered) + time[x][t || "input"] = rendered.time + } catch (e) { // clear pfp and throw if error encountered + await rm(userAvatarDirectory, { recursive: true, force: true }) + throw e + } } } diff --git a/src/lib/configuration.ts b/src/lib/configuration.ts index 5245db6..71cd8c5 100644 --- a/src/lib/configuration.ts +++ b/src/lib/configuration.ts @@ -1,3 +1,4 @@ +import { format, type FormatEnum } from "sharp" const configuration = { oauth2: { endpoints: { @@ -15,6 +16,23 @@ const configuration = { route: process.env.USERINFO__ROUTE!, identifier: process.env.USERINFO__IDENTIFIER! }, - allowed_types: process.env.ALLOWED_TYPES?.split(",") || [] + images: { + permitted_input: + ( + process.env.ALLOWED_TYPES + || process.env.IMAGES__ALLOWED_INPUT_FORMATS + )?.split(",") || [], + default_resolution: parseInt((process.env.IMAGES__DEFAULT_RESOLUTION || "").toString(), 10) || 512, + extra_output_types: + process.env.IMAGES__EXTRA_OUTPUT_FORMATS + ?.split(",") + .filter(e => e in format) as (keyof FormatEnum)[] + || [], + output_resolutions: + process.env.IMAGES__OUTPUT_RESOLUTIONS + ?.split(",") + .map(e => parseInt(e,10)) + || [ 1024, 512, 256, 128, 64, 32 ] + } } export default configuration \ No newline at end of file diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 548890f..0d35c12 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -4,6 +4,8 @@ import ava from "../assets/ava_icon.svg?raw" import type { User } from "$lib/types"; export let data: { user?: User }; + + const buildName = `${__APP_NAME__} ${__APP_VERSION__}` ava @@ -50,12 +52,16 @@ nav > * { display: flex; /* Flexbox fixes everything! */ } - a, small { + a, small, footer { color: var(--link) } code, pre { font-family: "Space Mono", monospace, monospace; } + footer { + margin: 1em 0; + text-align: center; + } @@ -69,4 +75,9 @@ + +
+ {import.meta.env.DEV ? "[DEV]" : ""} + {buildName} +
\ No newline at end of file diff --git a/src/routes/avatar/[identifier]/[[size]]/+server.ts b/src/routes/avatar/[identifier]/[[size]]/+server.ts index 3662a11..fa0e36d 100644 --- a/src/routes/avatar/[identifier]/[[size]]/+server.ts +++ b/src/routes/avatar/[identifier]/[[size]]/+server.ts @@ -1,11 +1,15 @@ import { getPathToAvatarForIdentifier } from '$lib/avatars.js'; import { error } from '@sveltejs/kit'; -export async function GET({ params : { identifier, size } }) { - let avPath = await getPathToAvatarForIdentifier(identifier, size) +export async function GET({ params : { identifier, size }, url }) { + let sz = size ? parseInt(size, 10) : undefined + if (sz && Number.isNaN(sz)) + throw error(400, "Invalid number") + + let avPath = await getPathToAvatarForIdentifier(identifier, sz, url.searchParams.get("format") || undefined) if (!avPath) - throw error(404, "Avatar at this size not found") + throw error(404, "Avatar at this size not found, or this is an invalid format") return new Response(Bun.file(avPath)) } \ No newline at end of file diff --git a/src/routes/set/+page.server.ts b/src/routes/set/+page.server.ts index 892bb37..92f6b14 100644 --- a/src/routes/set/+page.server.ts +++ b/src/routes/set/+page.server.ts @@ -1,7 +1,7 @@ import {getRequestUser, launchLogin} from "$lib/oidc" import configuration from "$lib/configuration.js"; import { fail } from "@sveltejs/kit"; -import { avatarDirectory, renderSizes, setNewAvatar } from "$lib/avatars.js"; +import { avatarDirectory, setNewAvatar } from "$lib/avatars.js"; import { join } from "path"; export async function load({ request, parent, url }) { const { user } = await parent(); @@ -10,8 +10,8 @@ export async function load({ request, parent, url }) { return { url: url.toString(), - allowedImageTypes: configuration.allowed_types, - renderSizes + allowedImageTypes: configuration.images.permitted_input, + renderSizes: configuration.images.output_resolutions } } @@ -27,15 +27,17 @@ export const actions = { newAvatar = submission.get("newAvatar") if (newAvatar !== undefined && !(newAvatar instanceof File)) return fail(400, {success: false, error: "incorrect entry type"}) - if (!configuration.allowed_types.includes(newAvatar.type)) + if (!configuration.images.permitted_input.includes(newAvatar.type)) return fail(400, {success: false, error: `allowed types does not include ${newAvatar.type}`}) } - let time = await setNewAvatar(user.sub, newAvatar) + let timing = await setNewAvatar(user.sub, newAvatar) return { success: true, - message: Object.entries(time) - .map(([res, time]) => `${res}x${res} took ${time}ms to render`) + message: Object.entries(timing) + .map(([res, time]) => `render ${res}x${res}: ${ + Object.entries(time).map(([type, t]) => `${type}: ${t}ms`).join(", ") + }`) .join("\n") || "No timing information available" } diff --git a/static/default/128.png b/static/default/128.png deleted file mode 100644 index de2974cca01f9eeedad68a9132828e2684f8e036..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4204 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 Xl!gk6)4<0+L#nP8RF^ diff --git a/static/default/256.png b/static/default/256.png deleted file mode 100644 index 2c114b4cb5601ceda4a1ebb85f6ede26125b1ca3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8189 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 diff --git a/static/default/512.png b/static/default/512.png deleted file mode 100644 index 2a8f6813101ae5a221cb83cd0a845b237a32ac31..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14625 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 diff --git a/static/default/index.png b/static/default/index.png deleted file mode 100644 index 2a8f6813101ae5a221cb83cd0a845b237a32ac31..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14625 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