Skip to content

Commit 6a73fd6

Browse files
yu256syuilotamaina
committed
enhance: AVIF support (misskey-dev#9281)
* chore: Make image/avif browsersafe * server side * change FileInfoService * ✌️ * avifはMastodonでは絶望的 see misskey-dev#9283 Co-authored-by: syuilo <[email protected]> Co-authored-by: tamaina <[email protected]>
1 parent 416fa05 commit 6a73fd6

File tree

8 files changed

+38
-10
lines changed

8 files changed

+38
-10
lines changed

packages/backend/src/const.ts

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export const FILE_TYPE_BROWSERSAFE = [
1212
'image/gif',
1313
'image/jpeg',
1414
'image/webp',
15+
'image/avif',
1516
'image/apng',
1617
'image/bmp',
1718
'image/tiff',

packages/backend/src/misc/get-file-info.ts

+26-3
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,18 @@ export async function getFileInfo(path: string, opts: {
6363
let height: number | undefined;
6464
let orientation: number | undefined;
6565

66-
if (['image/jpeg', 'image/gif', 'image/png', 'image/apng', 'image/webp', 'image/bmp', 'image/tiff', 'image/svg+xml', 'image/vnd.adobe.photoshop'].includes(type.mime)) {
66+
if ([
67+
'image/png',
68+
'image/gif',
69+
'image/jpeg',
70+
'image/webp',
71+
'image/avif',
72+
'image/apng',
73+
'image/bmp',
74+
'image/tiff',
75+
'image/svg+xml',
76+
'image/vnd.adobe.photoshop',
77+
].includes(type.mime)) {
6778
const imageSize = await detectImageSize(path).catch(e => {
6879
warnings.push(`detectImageSize failed: ${e}`);
6980
return undefined;
@@ -90,7 +101,15 @@ export async function getFileInfo(path: string, opts: {
90101

91102
let blurhash: string | undefined;
92103

93-
if (['image/jpeg', 'image/gif', 'image/png', 'image/apng', 'image/webp', 'image/svg+xml'].includes(type.mime)) {
104+
if ([
105+
'image/jpeg',
106+
'image/gif',
107+
'image/png',
108+
'image/apng',
109+
'image/webp',
110+
'image/avif',
111+
'image/svg+xml',
112+
].includes(type.mime)) {
94113
blurhash = await getBlurhash(path).catch(e => {
95114
warnings.push(`getBlurhash failed: ${e}`);
96115
return undefined;
@@ -145,7 +164,11 @@ async function detectSensitivity(source: string, mime: string, sensitiveThreshol
145164
return [sensitive, porn];
146165
}
147166

148-
if (['image/jpeg', 'image/png', 'image/webp'].includes(mime)) {
167+
if ([
168+
'image/jpeg',
169+
'image/png',
170+
'image/webp',
171+
].includes(mime)) {
149172
const result = await detectSensitive(source);
150173
if (result) {
151174
[sensitive, porn] = judgePrediction(result);

packages/backend/src/misc/is-mime-image.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { FILE_TYPE_BROWSERSAFE } from '@/const.js';
22

33
const dictionary = {
44
'safe-file': FILE_TYPE_BROWSERSAFE,
5-
'sharp-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp', 'image/svg+xml'],
5+
'sharp-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp', 'image/avif', 'image/svg+xml'],
66
};
77

88
export const isMimeImage = (mime: string, type: keyof typeof dictionary): boolean => dictionary[type].includes(mime);

packages/backend/src/models/repositories/drive-file.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
6161
}
6262
}
6363

64-
const isImage = file.type && ['image/png', 'image/apng', 'image/gif', 'image/jpeg', 'image/webp', 'image/svg+xml'].includes(file.type);
64+
const isImage = file.type && ['image/png', 'image/apng', 'image/gif', 'image/jpeg', 'image/webp', 'image/avif', 'image/svg+xml'].includes(file.type);
6565

6666
return thumbnail ? (file.thumbnailUrl || (isImage ? (file.webpublicUrl || file.url) : null)) : (file.webpublicUrl || file.url);
6767
},

packages/backend/src/server/file/send-drive-file.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export default async function(ctx: Koa.Context) {
5959

6060
const convertFile = async () => {
6161
if (isThumbnail) {
62-
if (['image/jpeg', 'image/webp', 'image/png', 'image/svg+xml'].includes(mime)) {
62+
if (['image/jpeg', 'image/webp', 'image/png', 'image/avif', 'image/svg+xml'].includes(mime)) {
6363
return await convertToWebp(path, 498, 280);
6464
} else if (mime.startsWith('video/')) {
6565
return await GenerateVideoThumbnail(path);

packages/backend/src/services/drive/add-file.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ async function save(file: DriveFile, path: string, name: string, type: string, h
4848
if (type === 'image/jpeg') ext = '.jpg';
4949
if (type === 'image/png') ext = '.png';
5050
if (type === 'image/webp') ext = '.webp';
51+
if (type === 'image/avif') ext = '.avif';
5152
if (type === 'image/apng') ext = '.apng';
5253
if (type === 'image/vnd.mozilla.apng') ext = '.apng';
5354
}
@@ -171,7 +172,7 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
171172
}
172173
}
173174

174-
if (!['image/jpeg', 'image/png', 'image/webp', 'image/svg+xml'].includes(type)) {
175+
if (!['image/jpeg', 'image/png', 'image/webp', 'image/avif', 'image/svg+xml'].includes(type)) {
175176
logger.debug('web image and thumbnail not created (not an required file)');
176177
return {
177178
webpublic: null,
@@ -196,7 +197,7 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
196197
}
197198

198199
satisfyWebpublic = !!(
199-
type !== 'image/svg+xml' && type !== 'image/webp' &&
200+
type !== 'image/svg+xml' && type !== 'image/webp' && type !== 'image/avif' &&
200201
!(metadata.exif || metadata.iptc || metadata.xmp || metadata.tifftagPhotoshop) &&
201202
metadata.width && metadata.width <= 2048 &&
202203
metadata.height && metadata.height <= 2048
@@ -216,7 +217,7 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
216217
logger.info('creating web image');
217218

218219
try {
219-
if (['image/jpeg', 'image/webp'].includes(type)) {
220+
if (['image/jpeg', 'image/webp', 'image/avif'].includes(type)) {
220221
webpublic = await convertSharpToJpeg(img, 2048, 2048);
221222
} else if (['image/png'].includes(type)) {
222223
webpublic = await convertSharpToPng(img, 2048, 2048);
@@ -238,7 +239,7 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
238239
let thumbnail: IImage | null = null;
239240

240241
try {
241-
if (['image/jpeg', 'image/webp', 'image/png', 'image/svg+xml'].includes(type)) {
242+
if (['image/jpeg', 'image/webp', 'image/avif', 'image/png', 'image/svg+xml'].includes(type)) {
242243
thumbnail = await convertSharpToWebp(img, 498, 280);
243244
} else {
244245
logger.debug('thumbnail not created (not an required file)');

packages/client/src/const.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const FILE_TYPE_BROWSERSAFE = [
77
'image/gif',
88
'image/jpeg',
99
'image/webp',
10+
'image/avif',
1011
'image/apng',
1112
'image/bmp',
1213
'image/tiff',

packages/client/src/pages/user/index.photos.vue

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ function thumbnail(image: misskey.entities.DriveFile): string {
4747
onMounted(() => {
4848
const image = [
4949
'image/jpeg',
50+
'image/webp',
51+
'image/avif',
5052
'image/png',
5153
'image/gif',
5254
'image/apng',

0 commit comments

Comments
 (0)