From 3d8bbedf1bbd501d3ad5e3c26e5e5005ed2d8371 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 15 May 2019 21:27:20 +0900
Subject: [PATCH] =?UTF-8?q?GIF=E3=81=AE=E3=82=B5=E3=83=A0=E3=83=8D?=
 =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=81=8C=E7=94=9F=E6=88=90=E3=81=95=E3=82=8C?=
 =?UTF-8?q?=E3=81=AA=E3=81=84=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

#4728
---
 src/server/proxy/proxy-media.ts               |  6 ++--
 src/services/drive/add-file.ts                | 18 ++++++----
 .../drive/generate-video-thumbnail.ts         |  4 +--
 src/services/drive/image-processor.ts         | 33 +++++++++++++++++--
 4 files changed, 47 insertions(+), 14 deletions(-)

diff --git a/src/server/proxy/proxy-media.ts b/src/server/proxy/proxy-media.ts
index 357715bb92..e16665f6cd 100644
--- a/src/server/proxy/proxy-media.ts
+++ b/src/server/proxy/proxy-media.ts
@@ -1,7 +1,7 @@
 import * as fs from 'fs';
 import * as Koa from 'koa';
 import { serverLogger } from '..';
-import { IImage, ConvertToPng, ConvertToJpeg } from '../../services/drive/image-processor';
+import { IImage, convertToPng, convertToJpeg } from '../../services/drive/image-processor';
 import { createTemp } from '../../misc/create-temp';
 import { downloadUrl } from '../../misc/donwload-url';
 import { detectMine } from '../../misc/detect-mine';
@@ -20,9 +20,9 @@ export async function proxyMedia(ctx: Koa.BaseContext) {
 		let image: IImage;
 
 		if ('static' in ctx.query && ['image/png', 'image/gif'].includes(type)) {
-			image = await ConvertToPng(path, 498, 280);
+			image = await convertToPng(path, 498, 280);
 		} else if ('preview' in ctx.query && ['image/jpeg', 'image/png', 'image/gif'].includes(type)) {
-			image = await ConvertToJpeg(path, 200, 200);
+			image = await convertToJpeg(path, 200, 200);
 		} else {
 			image = {
 				data: fs.readFileSync(path),
diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts
index c67ee475a8..949089eded 100644
--- a/src/services/drive/add-file.ts
+++ b/src/services/drive/add-file.ts
@@ -12,7 +12,7 @@ import config from '../../config';
 import { fetchMeta } from '../../misc/fetch-meta';
 import { GenerateVideoThumbnail } from './generate-video-thumbnail';
 import { driveLogger } from './logger';
-import { IImage, ConvertToJpeg, ConvertToWebp, ConvertToPng } from './image-processor';
+import { IImage, convertToJpeg, convertToWebp, convertToPng, convertToGif, convertToApng } from './image-processor';
 import { contentDisposition } from '../../misc/content-disposition';
 import { detectMine } from '../../misc/detect-mine';
 import { DriveFiles, DriveFolders, Users, Instances, UserProfiles } from '../../models';
@@ -149,11 +149,15 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
 		logger.info(`creating web image`);
 
 		if (['image/jpeg'].includes(type)) {
-			webpublic = await ConvertToJpeg(path, 2048, 2048);
+			webpublic = await convertToJpeg(path, 2048, 2048);
 		} else if (['image/webp'].includes(type)) {
-			webpublic = await ConvertToWebp(path, 2048, 2048);
+			webpublic = await convertToWebp(path, 2048, 2048);
 		} else if (['image/png'].includes(type)) {
-			webpublic = await ConvertToPng(path, 2048, 2048);
+			webpublic = await convertToPng(path, 2048, 2048);
+		} else if (['image/apng', 'image/vnd.mozilla.apng'].includes(type)) {
+			webpublic = await convertToApng(path);
+		} else if (['image/gif'].includes(type)) {
+			webpublic = await convertToGif(path);
 		} else {
 			logger.info(`web image not created (not an image)`);
 		}
@@ -166,9 +170,11 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
 	let thumbnail: IImage | null = null;
 
 	if (['image/jpeg', 'image/webp'].includes(type)) {
-		thumbnail = await ConvertToJpeg(path, 498, 280);
+		thumbnail = await convertToJpeg(path, 498, 280);
 	} else if (['image/png'].includes(type)) {
-		thumbnail = await ConvertToPng(path, 498, 280);
+		thumbnail = await convertToPng(path, 498, 280);
+	} else if (['image/gif'].includes(type)) {
+		thumbnail = await convertToGif(path);
 	} else if (type.startsWith('video/')) {
 		try {
 			thumbnail = await GenerateVideoThumbnail(path);
diff --git a/src/services/drive/generate-video-thumbnail.ts b/src/services/drive/generate-video-thumbnail.ts
index 5d7efff27b..c2646182db 100644
--- a/src/services/drive/generate-video-thumbnail.ts
+++ b/src/services/drive/generate-video-thumbnail.ts
@@ -1,6 +1,6 @@
 import * as fs from 'fs';
 import * as tmp from 'tmp';
-import { IImage, ConvertToJpeg } from './image-processor';
+import { IImage, convertToJpeg } from './image-processor';
 const ThumbnailGenerator = require('video-thumbnail-generator').default;
 
 export async function GenerateVideoThumbnail(path: string): Promise<IImage> {
@@ -23,7 +23,7 @@ export async function GenerateVideoThumbnail(path: string): Promise<IImage> {
 
 	const outPath = `${outDir}/output.png`;
 
-	const thumbnail = await ConvertToJpeg(outPath, 498, 280);
+	const thumbnail = await convertToJpeg(outPath, 498, 280);
 
 	// cleanup
 	fs.unlinkSync(outPath);
diff --git a/src/services/drive/image-processor.ts b/src/services/drive/image-processor.ts
index 89ac331ca1..4b8db0e0c8 100644
--- a/src/services/drive/image-processor.ts
+++ b/src/services/drive/image-processor.ts
@@ -1,4 +1,5 @@
 import * as sharp from 'sharp';
+import * as fs from 'fs';
 
 export type IImage = {
 	data: Buffer;
@@ -10,7 +11,7 @@ export type IImage = {
  * Convert to JPEG
  *   with resize, remove metadata, resolve orientation, stop animation
  */
-export async function ConvertToJpeg(path: string, width: number, height: number): Promise<IImage> {
+export async function convertToJpeg(path: string, width: number, height: number): Promise<IImage> {
 	const data = await sharp(path)
 		.resize(width, height, {
 			fit: 'inside',
@@ -34,7 +35,7 @@ export async function ConvertToJpeg(path: string, width: number, height: number)
  * Convert to WebP
  *   with resize, remove metadata, resolve orientation, stop animation
  */
-export async function ConvertToWebp(path: string, width: number, height: number): Promise<IImage> {
+export async function convertToWebp(path: string, width: number, height: number): Promise<IImage> {
 	const data = await sharp(path)
 		.resize(width, height, {
 			fit: 'inside',
@@ -57,7 +58,7 @@ export async function ConvertToWebp(path: string, width: number, height: number)
  * Convert to PNG
  *   with resize, remove metadata, resolve orientation, stop animation
  */
-export async function ConvertToPng(path: string, width: number, height: number): Promise<IImage> {
+export async function convertToPng(path: string, width: number, height: number): Promise<IImage> {
 	const data = await sharp(path)
 		.resize(width, height, {
 			fit: 'inside',
@@ -73,3 +74,29 @@ export async function ConvertToPng(path: string, width: number, height: number):
 		type: 'image/png'
 	};
 }
+
+/**
+ * Convert to GIF (Actually just NOP)
+ */
+export async function convertToGif(path: string): Promise<IImage> {
+	const data = await fs.promises.readFile(path);
+
+	return {
+		data,
+		ext: 'gif',
+		type: 'image/gif'
+	};
+}
+
+/**
+ * Convert to APNG (Actually just NOP)
+ */
+export async function convertToApng(path: string): Promise<IImage> {
+	const data = await fs.promises.readFile(path);
+
+	return {
+		data,
+		ext: 'apng',
+		type: 'image/apng'
+	};
+}