diff --git a/built/file-info.js b/built/file-info.js index ff1cc3d..a55f419 100644 --- a/built/file-info.js +++ b/built/file-info.js @@ -55,7 +55,7 @@ async function checkSvg(path) { import { FILE_TYPE_BROWSERSAFE } from './const.js'; const dictionary = { 'safe-file': FILE_TYPE_BROWSERSAFE, - 'sharp-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp', 'image/avif', 'image/svg+xml'], - 'sharp-animation-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/avif', 'image/svg+xml'], + 'sharp-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp', 'image/avif', 'image/svg+xml', 'image/x-icon', 'image/bmp'], + 'sharp-animation-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/avif', 'image/svg+xml', 'image/x-icon', 'image/bmp'], }; export const isMimeImage = (mime, type) => dictionary[type].includes(mime); diff --git a/built/index.js b/built/index.js index a0a1305..df114ea 100644 --- a/built/index.js +++ b/built/index.js @@ -4,9 +4,10 @@ import { dirname } from 'node:path'; import fastifyStatic from '@fastify/static'; import { createTemp } from './create-temp.js'; import { FILE_TYPE_BROWSERSAFE } from './const.js'; -import { convertToWebpStream, webpDefault } from './image-processor.js'; +import { convertToWebpStream, webpDefault, convertSharpToWebpStream } from './image-processor.js'; import { detectType, isMimeImage } from './file-info.js'; import sharp from 'sharp'; +import { sharpBmp } from 'sharp-read-bmp'; import { StatusError } from './status-error.js'; import { defaultDownloadConfig, downloadUrl } from './download.js'; import { getAgents } from './http.js'; @@ -107,7 +108,7 @@ async function proxyHandler(request, reply) { }; } else { - const data = sharp(file.path, { animated: !('static' in request.query) }) + const data = (await sharpBmp(file.path, file.mime, { animated: !('static' in request.query) })) .resize({ height: 'emoji' in request.query ? 128 : 320, withoutEnlargement: true, @@ -121,13 +122,13 @@ async function proxyHandler(request, reply) { } } else if ('static' in request.query) { - image = convertToWebpStream(file.path, 498, 280); + image = convertSharpToWebpStream(await sharpBmp(file.path, file.mime), 498, 280); } else if ('preview' in request.query) { - image = convertToWebpStream(file.path, 200, 200); + image = convertSharpToWebpStream(await sharpBmp(file.path, file.mime), 200, 200); } else if ('badge' in request.query) { - const mask = sharp(file.path) + const mask = (await sharpBmp(file.path, file.mime)) .resize(96, 96, { fit: 'inside', withoutEnlargement: false, diff --git a/package.json b/package.json index 16fe28c..b274024 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "misskey-media-proxy", - "version": "0.0.16", + "version": "0.0.17", "description": "The Media Proxy for Misskey", "main": "built/index.js", - "packageManager": "pnpm@7.26.0", + "packageManager": "pnpm@7.28.0", "type": "module", "files": [ "built", @@ -47,6 +47,7 @@ "is-svg": "^4.3.2", "private-ip": "^3.0.0", "sharp": "^0.31.3", + "sharp-read-bmp": "github:misskey-dev/sharp-read-bmp", "tmp": "^0.2.1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 700c683..70d9acc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,6 +19,7 @@ specifiers: is-svg: ^4.3.2 private-ip: ^3.0.0 sharp: ^0.31.3 + sharp-read-bmp: github:misskey-dev/sharp-read-bmp tmp: ^0.2.1 typescript: ^4.9.5 @@ -35,6 +36,7 @@ dependencies: is-svg: 4.3.2 private-ip: 3.0.0 sharp: 0.31.3 + sharp-read-bmp: github.com/misskey-dev/sharp-read-bmp/c5fb82e26fa50fca8b3b2d7e22467626d7f61baa tmp: 0.2.1 devDependencies: @@ -48,6 +50,10 @@ devDependencies: packages: + /@canvas/image-data/1.0.0: + resolution: {integrity: sha512-BxOqI5LgsIQP1odU5KMwV9yoijleOPzHL18/YvNqF9KFSGF2K/DLlYAbDQsWqd/1nbaFuSkYD/191dpMtNh4vw==} + dev: false + /@chainsafe/is-ip/2.0.1: resolution: {integrity: sha512-nqSJ8u2a1Rv9FYbyI8qpDhTYujaKEyLknNrTejLYoSWmdeg+2WB7R6BZqPZYfrJzDxVi3rl6ZQuoaEvpKRZWgQ==} dev: false @@ -643,6 +649,23 @@ packages: ms: 2.1.2 dev: false + /decode-bmp/0.2.1: + resolution: {integrity: sha512-NiOaGe+GN0KJqi2STf24hfMkFitDUaIoUU3eKvP/wAbLe8o6FuW5n/x7MHPR0HKvBokp6MQY/j7w8lewEeVCIA==} + engines: {node: '>=8.6.0'} + dependencies: + '@canvas/image-data': 1.0.0 + to-data-view: 1.1.0 + dev: false + + /decode-ico/0.4.1: + resolution: {integrity: sha512-69NZfbKIzux1vBOd31al3XnMnH+2mqDhEgLdpygErm4d60N+UwA5Sq5WFjmEDQzumgB9fElojGwWG0vybVfFmA==} + engines: {node: '>=8.6'} + dependencies: + '@canvas/image-data': 1.0.0 + decode-bmp: 0.2.1 + to-data-view: 1.1.0 + dev: false + /decompress-response/6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -1978,6 +2001,10 @@ packages: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} dev: false + /to-data-view/1.1.0: + resolution: {integrity: sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ==} + dev: false + /to-regex-range/5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -2064,3 +2091,13 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: false + + github.com/misskey-dev/sharp-read-bmp/c5fb82e26fa50fca8b3b2d7e22467626d7f61baa: + resolution: {tarball: https://codeload.github.com/misskey-dev/sharp-read-bmp/tar.gz/c5fb82e26fa50fca8b3b2d7e22467626d7f61baa} + name: sharp-read-bmp + version: 1.0.0 + dependencies: + decode-bmp: 0.2.1 + decode-ico: 0.4.1 + sharp: 0.31.3 + dev: false diff --git a/src/file-info.ts b/src/file-info.ts index e5f888e..c14bdfd 100644 --- a/src/file-info.ts +++ b/src/file-info.ts @@ -66,8 +66,8 @@ import { FILE_TYPE_BROWSERSAFE } from './const.js'; const dictionary = { 'safe-file': FILE_TYPE_BROWSERSAFE, - 'sharp-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp', 'image/avif', 'image/svg+xml'], - 'sharp-animation-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/avif', 'image/svg+xml'], + 'sharp-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp', 'image/avif', 'image/svg+xml', 'image/x-icon', 'image/bmp'], + 'sharp-animation-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/avif', 'image/svg+xml', 'image/x-icon', 'image/bmp'], }; export const isMimeImage = (mime: string, type: keyof typeof dictionary): boolean => dictionary[type].includes(mime); diff --git a/src/index.ts b/src/index.ts index 2bb616e..0db33b3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,10 +6,11 @@ import { dirname } from 'node:path'; import fastifyStatic from '@fastify/static'; import { createTemp } from './create-temp.js'; import { FILE_TYPE_BROWSERSAFE } from './const.js'; -import { IImageStreamable, convertToWebpStream, webpDefault } from './image-processor.js'; +import { IImageStreamable, convertToWebpStream, webpDefault, convertSharpToWebpStream } from './image-processor.js'; import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify'; import { detectType, isMimeImage } from './file-info.js'; import sharp from 'sharp'; +import { sharpBmp } from 'sharp-read-bmp'; import { StatusError } from './status-error.js'; import { DownloadConfig, defaultDownloadConfig, downloadUrl } from './download.js'; import { getAgents } from './http.js'; @@ -153,12 +154,12 @@ async function proxyHandler(request: FastifyRequest<{ Params: { url: string; }; type: file.mime, }; } else { - const data = sharp(file.path, { animated: !('static' in request.query) }) - .resize({ - height: 'emoji' in request.query ? 128 : 320, - withoutEnlargement: true, - }) - .webp(webpDefault); + const data = (await sharpBmp(file.path, file.mime, { animated: !('static' in request.query) })) + .resize({ + height: 'emoji' in request.query ? 128 : 320, + withoutEnlargement: true, + }) + .webp(webpDefault); image = { data, @@ -167,11 +168,11 @@ async function proxyHandler(request: FastifyRequest<{ Params: { url: string; }; }; } } else if ('static' in request.query) { - image = convertToWebpStream(file.path, 498, 280); + image = convertSharpToWebpStream(await sharpBmp(file.path, file.mime), 498, 280); } else if ('preview' in request.query) { - image = convertToWebpStream(file.path, 200, 200); + image = convertSharpToWebpStream(await sharpBmp(file.path, file.mime), 200, 200); } else if ('badge' in request.query) { - const mask = sharp(file.path) + const mask = (await sharpBmp(file.path, file.mime)) .resize(96, 96, { fit: 'inside', withoutEnlargement: false,