enhance: AVIF support (#9281)
* chore: Make image/avif browsersafe * server side * change FileInfoService * ✌️ * avifはMastodonでは絶望的 see https://github.com/misskey-dev/misskey/issues/9283 Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
This commit is contained in:
		
							parent
							
								
									bd35d0fb2a
								
							
						
					
					
						commit
						e81c2962a0
					
				
					 8 changed files with 38 additions and 10 deletions
				
			
		|  | @ -12,6 +12,7 @@ export const FILE_TYPE_BROWSERSAFE = [ | |||
| 	'image/gif', | ||||
| 	'image/jpeg', | ||||
| 	'image/webp', | ||||
| 	'image/avif', | ||||
| 	'image/apng', | ||||
| 	'image/bmp', | ||||
| 	'image/tiff', | ||||
|  |  | |||
|  | @ -138,6 +138,7 @@ export class DriveService { | |||
| 				if (type === 'image/jpeg') ext = '.jpg'; | ||||
| 				if (type === 'image/png') ext = '.png'; | ||||
| 				if (type === 'image/webp') ext = '.webp'; | ||||
| 				if (type === 'image/avif') ext = '.avif'; | ||||
| 				if (type === 'image/apng') ext = '.apng'; | ||||
| 				if (type === 'image/vnd.mozilla.apng') ext = '.apng'; | ||||
| 			} | ||||
|  | @ -262,7 +263,7 @@ export class DriveService { | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (!['image/jpeg', 'image/png', 'image/webp', 'image/svg+xml'].includes(type)) { | ||||
| 		if (!['image/jpeg', 'image/png', 'image/webp', 'image/avif', 'image/svg+xml'].includes(type)) { | ||||
| 			this.registerLogger.debug('web image and thumbnail not created (not an required file)'); | ||||
| 			return { | ||||
| 				webpublic: null, | ||||
|  | @ -287,7 +288,7 @@ export class DriveService { | |||
| 			} | ||||
| 
 | ||||
| 			satisfyWebpublic = !!( | ||||
| 				type !== 'image/svg+xml' && type !== 'image/webp' && | ||||
| 				type !== 'image/svg+xml' && type !== 'image/webp' && type !== 'image/avif' && | ||||
| 			!(metadata.exif ?? metadata.iptc ?? metadata.xmp ?? metadata.tifftagPhotoshop) && | ||||
| 			metadata.width && metadata.width <= 2048 && | ||||
| 			metadata.height && metadata.height <= 2048 | ||||
|  | @ -307,7 +308,7 @@ export class DriveService { | |||
| 			this.registerLogger.info('creating web image'); | ||||
| 
 | ||||
| 			try { | ||||
| 				if (['image/jpeg', 'image/webp'].includes(type)) { | ||||
| 				if (['image/jpeg', 'image/webp', 'image/avif'].includes(type)) { | ||||
| 					webpublic = await this.imageProcessingService.convertSharpToJpeg(img, 2048, 2048); | ||||
| 				} else if (['image/png'].includes(type)) { | ||||
| 					webpublic = await this.imageProcessingService.convertSharpToPng(img, 2048, 2048); | ||||
|  | @ -329,7 +330,7 @@ export class DriveService { | |||
| 		let thumbnail: IImage | null = null; | ||||
| 
 | ||||
| 		try { | ||||
| 			if (['image/jpeg', 'image/webp', 'image/png', 'image/svg+xml'].includes(type)) { | ||||
| 			if (['image/jpeg', 'image/webp', 'image/avif', 'image/png', 'image/svg+xml'].includes(type)) { | ||||
| 				thumbnail = await this.imageProcessingService.convertSharpToWebp(img, 498, 280); | ||||
| 			} else { | ||||
| 				this.registerLogger.debug('thumbnail not created (not an required file)'); | ||||
|  |  | |||
|  | @ -73,7 +73,18 @@ export class FileInfoService { | |||
| 		let height: number | undefined; | ||||
| 		let orientation: number | undefined; | ||||
| 
 | ||||
| 		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)) { | ||||
| 		if ([ | ||||
| 			'image/png', | ||||
| 			'image/gif', | ||||
| 			'image/jpeg', | ||||
| 			'image/webp', | ||||
| 			'image/avif', | ||||
| 			'image/apng', | ||||
| 			'image/bmp', | ||||
| 			'image/tiff', | ||||
| 			'image/svg+xml', | ||||
| 			'image/vnd.adobe.photoshop', | ||||
| 		].includes(type.mime)) { | ||||
| 			const imageSize = await this.detectImageSize(path).catch(e => { | ||||
| 				warnings.push(`detectImageSize failed: ${e}`); | ||||
| 				return undefined; | ||||
|  | @ -100,7 +111,15 @@ export class FileInfoService { | |||
| 
 | ||||
| 		let blurhash: string | undefined; | ||||
| 
 | ||||
| 		if (['image/jpeg', 'image/gif', 'image/png', 'image/apng', 'image/webp', 'image/svg+xml'].includes(type.mime)) { | ||||
| 		if ([ | ||||
| 			'image/jpeg', | ||||
| 			'image/gif', | ||||
| 			'image/png', | ||||
| 			'image/apng', | ||||
| 			'image/webp', | ||||
| 			'image/avif', | ||||
| 			'image/svg+xml', | ||||
| 		].includes(type.mime)) { | ||||
| 			blurhash = await this.getBlurhash(path).catch(e => { | ||||
| 				warnings.push(`getBlurhash failed: ${e}`); | ||||
| 				return undefined; | ||||
|  | @ -156,7 +175,11 @@ export class FileInfoService { | |||
| 			return [sensitive, porn]; | ||||
| 		} | ||||
| 	 | ||||
| 		if (['image/jpeg', 'image/png', 'image/webp'].includes(mime)) { | ||||
| 		if ([ | ||||
| 			'image/jpeg', | ||||
| 			'image/png', | ||||
| 			'image/webp', | ||||
| 		].includes(mime)) { | ||||
| 			const result = await this.aiService.detectSensitive(source); | ||||
| 			if (result) { | ||||
| 				[sensitive, porn] = judgePrediction(result); | ||||
|  |  | |||
|  | @ -89,7 +89,7 @@ export class DriveFileEntityService { | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		const isImage = file.type && ['image/png', 'image/apng', 'image/gif', 'image/jpeg', 'image/webp', 'image/svg+xml'].includes(file.type); | ||||
| 		const isImage = file.type && ['image/png', 'image/apng', 'image/gif', 'image/jpeg', 'image/webp', 'image/avif', 'image/svg+xml'].includes(file.type); | ||||
| 
 | ||||
| 		return thumbnail ? (file.thumbnailUrl ?? (isImage ? (file.webpublicUrl ?? file.url) : null)) : (file.webpublicUrl ?? file.url); | ||||
| 	} | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ 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/svg+xml'], | ||||
| 	'sharp-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp', 'image/avif', 'image/svg+xml'], | ||||
| }; | ||||
| 
 | ||||
| export const isMimeImage = (mime: string, type: keyof typeof dictionary): boolean => dictionary[type].includes(mime); | ||||
|  |  | |||
|  | @ -114,7 +114,7 @@ export class FileServerService { | |||
| 
 | ||||
| 					const convertFile = async () => { | ||||
| 						if (isThumbnail) { | ||||
| 							if (['image/jpeg', 'image/webp', 'image/png', 'image/svg+xml'].includes(mime)) { | ||||
| 							if (['image/jpeg', 'image/webp', 'image/avif', 'image/png', 'image/svg+xml'].includes(mime)) { | ||||
| 								return await this.imageProcessingService.convertToWebp(path, 498, 280); | ||||
| 							} else if (mime.startsWith('video/')) { | ||||
| 								return await this.videoProcessingService.generateVideoThumbnail(path); | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ export const FILE_TYPE_BROWSERSAFE = [ | |||
| 	'image/gif', | ||||
| 	'image/jpeg', | ||||
| 	'image/webp', | ||||
| 	'image/avif', | ||||
| 	'image/apng', | ||||
| 	'image/bmp', | ||||
| 	'image/tiff', | ||||
|  |  | |||
|  | @ -47,6 +47,8 @@ function thumbnail(image: misskey.entities.DriveFile): string { | |||
| onMounted(() => { | ||||
| 	const image = [ | ||||
| 		'image/jpeg', | ||||
| 		'image/webp', | ||||
| 		'image/avif', | ||||
| 		'image/png', | ||||
| 		'image/gif', | ||||
| 		'image/apng', | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue