From 765980c4d59c30ce50d80cfb94646b9727919b05 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sat, 2 Feb 2019 13:22:09 +0900 Subject: [PATCH] Generate video thumbnails (#4084) * Generate video thumbnails * import order --- package.json | 1 + .../desktop/views/components/media-video.vue | 2 +- .../mobile/views/components/media-video.vue | 2 +- src/misc/get-drive-file-url.ts | 2 +- src/server/file/send-drive-file.ts | 7 +++- src/services/drive/add-file.ts | 9 ++++++ .../drive/generate-video-thumbnail.ts | 32 +++++++++++++++++++ 7 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 src/services/drive/generate-video-thumbnail.ts diff --git a/package.json b/package.json index bb9ef954e..a3573638c 100644 --- a/package.json +++ b/package.json @@ -226,6 +226,7 @@ "url-loader": "1.1.2", "uuid": "3.3.2", "v-animate-css": "0.0.3", + "video-thumbnail-generator": "1.1.3", "vue": "2.5.17", "vue-color": "2.7.0", "vue-content-loading": "1.5.3", diff --git a/src/client/app/desktop/views/components/media-video.vue b/src/client/app/desktop/views/components/media-video.vue index 4d73cfe3a..7898c4717 100644 --- a/src/client/app/desktop/views/components/media-video.vue +++ b/src/client/app/desktop/views/components/media-video.vue @@ -41,7 +41,7 @@ export default Vue.extend({ computed: { imageStyle(): any { return { - 'background-image': null // TODO `url(${this.video.thumbnailUrl})` + 'background-image': `url(${this.video.thumbnailUrl})` }; } }, diff --git a/src/client/app/mobile/views/components/media-video.vue b/src/client/app/mobile/views/components/media-video.vue index 8c4e9e153..1bf282b4a 100644 --- a/src/client/app/mobile/views/components/media-video.vue +++ b/src/client/app/mobile/views/components/media-video.vue @@ -35,7 +35,7 @@ export default Vue.extend({ computed: { imageStyle(): any { return { - 'background-image': null // TODO `url(${this.video.thumbnailUrl})` + 'background-image': `url(${this.video.thumbnailUrl})` }; } } diff --git a/src/misc/get-drive-file-url.ts b/src/misc/get-drive-file-url.ts index 90c3f2442..c52659522 100644 --- a/src/misc/get-drive-file-url.ts +++ b/src/misc/get-drive-file-url.ts @@ -14,7 +14,7 @@ export default function(file: IDriveFile, thumbnail = false): string { } } else { if (thumbnail) { - return isImage ? `${config.drive_url}/${file._id}?thumbnail` : null; + return `${config.drive_url}/${file._id}?thumbnail`; } else { return `${config.drive_url}/${file._id}?web`; } diff --git a/src/server/file/send-drive-file.ts b/src/server/file/send-drive-file.ts index 05d871a4c..1cfdd1c4d 100644 --- a/src/server/file/send-drive-file.ts +++ b/src/server/file/send-drive-file.ts @@ -64,7 +64,12 @@ export default async function(ctx: Koa.BaseContext) { const bucket = await getDriveFileThumbnailBucket(); ctx.body = bucket.openDownloadStream(thumb._id); } else { - await sendRaw(); + if (file.contentType.startsWith('image/')) { + await sendRaw(); + } else { + ctx.status = 404; + await send(ctx as any, '/dummy.png', { root: assets }); + } } } else if ('web' in ctx.query) { const web = await DriveFileWebpublic.findOne({ diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts index de67f2530..1d5f31c50 100644 --- a/src/services/drive/add-file.ts +++ b/src/services/drive/add-file.ts @@ -22,6 +22,7 @@ import { getDriveFileThumbnailBucket } from '../../models/drive-file-thumbnail'; import driveChart from '../../chart/drive'; import perUserDriveChart from '../../chart/per-user-drive'; import fetchMeta from '../../misc/fetch-meta'; +import { GenerateVideoThumbnail } from './generate-video-thumbnail'; const log = debug('misskey:drive:add-file'); @@ -118,6 +119,14 @@ async function save(path: string, name: string, type: string, hash: string, size thumbnailExt = 'png'; thumbnailType = 'image/png'; + } else if (type.startsWith('video/')) { + try { + thumbnail = await GenerateVideoThumbnail(path); + thumbnailExt = 'png'; + thumbnailType = 'image/png'; + } catch (e) { + console.log(`GenerateVideoThumbnail failed: ${e}`); + } } // #endregion thumbnail diff --git a/src/services/drive/generate-video-thumbnail.ts b/src/services/drive/generate-video-thumbnail.ts new file mode 100644 index 000000000..0ee69be4a --- /dev/null +++ b/src/services/drive/generate-video-thumbnail.ts @@ -0,0 +1,32 @@ +import * as fs from 'fs'; +import * as tmp from 'tmp'; +const ThumbnailGenerator = require('video-thumbnail-generator').default; + +export async function GenerateVideoThumbnail(path: string): Promise { + const [outDir, cleanup] = await new Promise<[string, any]>((res, rej) => { + tmp.dir((e, path, cleanup) => { + if (e) return rej(e); + res([path, cleanup]); + }); + }); + + const tg = new ThumbnailGenerator({ + sourcePath: path, + thumbnailPath: outDir, + }); + + await tg.generateOneByPercent(10, { + size: '498x280', + filename: 'output.png', + }); + + const outPath = `${outDir}/output.png`; + + const buffer = fs.readFileSync(outPath); + + // cleanup + fs.unlinkSync(outPath); + cleanup(); + + return buffer; +}