diff --git a/src/client/app/common/views/components/media-list.vue b/src/client/app/common/views/components/media-list.vue index 6c54967eb..bfbc9366d 100644 --- a/src/client/app/common/views/components/media-list.vue +++ b/src/client/app/common/views/components/media-list.vue @@ -42,7 +42,7 @@ export default Vue.extend({ }, methods: { previewable(file) { - return file.type.startsWith('video') || file.type.startsWith('image'); + return (file.type.startsWith('video') || file.type.startsWith('image')) && file.thumbnailUrl; } } }); diff --git a/src/server/file/send-drive-file.ts b/src/server/file/send-drive-file.ts index 1a43e5dc4..228343579 100644 --- a/src/server/file/send-drive-file.ts +++ b/src/server/file/send-drive-file.ts @@ -9,7 +9,7 @@ import { DriveFiles } from '../../models'; import { InternalStorage } from '../../services/drive/internal-storage'; import { downloadUrl } from '../../misc/donwload-url'; import { detectMine } from '../../misc/detect-mine'; -import { convertToJpeg, convertToPng, convertToGif, convertToApng } from '../../services/drive/image-processor'; +import { convertToJpeg, convertToPng } from '../../services/drive/image-processor'; import { GenerateVideoThumbnail } from '../../services/drive/generate-video-thumbnail'; const assets = `${__dirname}/../../server/file/assets/`; @@ -60,10 +60,6 @@ export default async function(ctx: Koa.Context) { return await convertToJpeg(path, 498, 280); } else if (['image/png'].includes(type)) { return await convertToPng(path, 498, 280); - } else if (['image/gif'].includes(type)) { - return await convertToGif(path); - } else if (['image/apng', 'image/vnd.mozilla.apng'].includes(type)) { - return await convertToApng(path); } else if (type.startsWith('video/')) { return await GenerateVideoThumbnail(path); } @@ -101,22 +97,23 @@ export default async function(ctx: Koa.Context) { return; } - if (isThumbnail) { + if (isThumbnail || isWebpublic) { + const [mime, ext] = await detectMine(InternalStorage.resolvePath(key)); + const filename = rename(file.name, { + suffix: isThumbnail ? '-thumb' : '-web', + extname: ext ? `.${ext}` : undefined + }).toString(); + ctx.body = InternalStorage.read(key); - ctx.set('Content-Type', 'image/jpeg'); + ctx.set('Content-Type', mime); ctx.set('Cache-Control', 'max-age=31536000, immutable'); - ctx.set('Content-Disposition', contentDisposition('inline', `${rename(file.name, { suffix: '-thumb', extname: '.jpeg' })}`)); - } else if (isWebpublic) { - ctx.body = InternalStorage.read(key); - ctx.set('Content-Type', file.type === 'image/apng' ? 'image/png' : file.type); - ctx.set('Cache-Control', 'max-age=31536000, immutable'); - ctx.set('Content-Disposition', contentDisposition('inline', `${rename(file.name, { suffix: '-web' })}`)); + ctx.set('Content-Disposition', contentDisposition('inline', filename)); } else { const readable = InternalStorage.read(file.accessKey!); readable.on('error', commonReadableHandlerGenerator(ctx)); ctx.body = readable; ctx.set('Content-Type', file.type); ctx.set('Cache-Control', 'max-age=31536000, immutable'); - ctx.set('Content-Disposition', contentDisposition('inline', `${file.name}`)); + ctx.set('Content-Disposition', contentDisposition('inline', file.name)); } } diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts index 54aa018a4..350e4dfe1 100644 --- a/src/services/drive/add-file.ts +++ b/src/services/drive/add-file.ts @@ -159,6 +159,8 @@ export async function generateAlts(path: string, type: string, generateWeb: bool webpublic = await convertToWebp(path, 2048, 2048); } else if (['image/png'].includes(type)) { webpublic = await convertToPng(path, 2048, 2048); + } else { + logger.debug(`web image not created (not an required image)`); } } catch (e) { logger.warn(`web image not created (an error occured)`, e); @@ -180,8 +182,10 @@ export async function generateAlts(path: string, type: string, generateWeb: bool try { thumbnail = await GenerateVideoThumbnail(path); } catch (e) { - logger.error(`GenerateVideoThumbnail failed: ${e}`); + logger.warn(`GenerateVideoThumbnail failed: ${e}`); } + } else { + logger.debug(`thumbnail not created (not an required file)`); } } catch (e) { logger.warn(`thumbnail not created (an error occured)`, e); @@ -351,7 +355,7 @@ export default async function( let propPromises: Promise[] = []; - const isImage = ['image/jpeg', 'image/gif', 'image/png', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp'].includes(mime); + const isImage = ['image/jpeg', 'image/gif', 'image/png', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp', 'image/svg+xml'].includes(mime); if (isImage) { const img = sharp(path); @@ -374,15 +378,21 @@ export default async function( logger.debug('calculating average color...'); try { - const info = await (img as any).stats(); + const info = await img.stats(); - const r = Math.round(info.channels[0].mean); - const g = Math.round(info.channels[1].mean); - const b = Math.round(info.channels[2].mean); + if (info.isOpaque) { + const r = Math.round(info.channels[0].mean); + const g = Math.round(info.channels[1].mean); + const b = Math.round(info.channels[2].mean); - logger.debug(`average color is calculated: ${r}, ${g}, ${b}`); + logger.debug(`average color is calculated: ${r}, ${g}, ${b}`); - properties['avgColor'] = `rgb(${r},${g},${b})`; + properties['avgColor'] = `rgb(${r},${g},${b})`; + } else { + logger.debug(`this image is not opaque so average color is 255, 255, 255`); + + properties['avgColor'] = `rgb(255,255,255)`; + } } catch (e) { } }; diff --git a/src/services/drive/image-processor.ts b/src/services/drive/image-processor.ts index e2dd90cfa..21a05fa9e 100644 --- a/src/services/drive/image-processor.ts +++ b/src/services/drive/image-processor.ts @@ -1,5 +1,4 @@ import * as sharp from 'sharp'; -import * as fs from 'fs'; export type IImage = { data: Buffer; diff --git a/src/services/drive/internal-storage.ts b/src/services/drive/internal-storage.ts index ff890d7d4..f8d7489a2 100644 --- a/src/services/drive/internal-storage.ts +++ b/src/services/drive/internal-storage.ts @@ -3,25 +3,27 @@ import * as Path from 'path'; import config from '../../config'; export class InternalStorage { - private static readonly path = Path.resolve(`${__dirname}/../../../files`); + private static readonly path = Path.resolve(__dirname, '../../../files'); + + public static resolvePath = (key: string) => Path.resolve(InternalStorage.path, key); public static read(key: string) { - return fs.createReadStream(`${InternalStorage.path}/${key}`); + return fs.createReadStream(InternalStorage.resolvePath(key)); } public static saveFromPath(key: string, srcPath: string) { fs.mkdirSync(InternalStorage.path, { recursive: true }); - fs.copyFileSync(srcPath, `${InternalStorage.path}/${key}`); + fs.copyFileSync(srcPath, InternalStorage.resolvePath(key)); return `${config.url}/files/${key}`; } public static saveFromBuffer(key: string, data: Buffer) { fs.mkdirSync(InternalStorage.path, { recursive: true }); - fs.writeFileSync(`${InternalStorage.path}/${key}`, data); + fs.writeFileSync(InternalStorage.resolvePath(key), data); return `${config.url}/files/${key}`; } public static del(key: string) { - fs.unlink(`${InternalStorage.path}/${key}`, () => {}); + fs.unlink(InternalStorage.resolvePath(key), () => {}); } }