Generate video thumbnails (#4084)

* Generate video thumbnails

* import order
This commit is contained in:
MeiMei 2019-02-02 13:22:09 +09:00 committed by syuilo
parent 170b1bb4cc
commit 3040700005
7 changed files with 51 additions and 4 deletions

View file

@ -226,6 +226,7 @@
"url-loader": "1.1.2", "url-loader": "1.1.2",
"uuid": "3.3.2", "uuid": "3.3.2",
"v-animate-css": "0.0.3", "v-animate-css": "0.0.3",
"video-thumbnail-generator": "1.1.3",
"vue": "2.5.17", "vue": "2.5.17",
"vue-color": "2.7.0", "vue-color": "2.7.0",
"vue-content-loading": "1.5.3", "vue-content-loading": "1.5.3",

View file

@ -41,7 +41,7 @@ export default Vue.extend({
computed: { computed: {
imageStyle(): any { imageStyle(): any {
return { return {
'background-image': null // TODO `url(${this.video.thumbnailUrl})` 'background-image': `url(${this.video.thumbnailUrl})`
}; };
} }
}, },

View file

@ -35,7 +35,7 @@ export default Vue.extend({
computed: { computed: {
imageStyle(): any { imageStyle(): any {
return { return {
'background-image': null // TODO `url(${this.video.thumbnailUrl})` 'background-image': `url(${this.video.thumbnailUrl})`
}; };
} }
} }

View file

@ -14,7 +14,7 @@ export default function(file: IDriveFile, thumbnail = false): string {
} }
} else { } else {
if (thumbnail) { if (thumbnail) {
return isImage ? `${config.drive_url}/${file._id}?thumbnail` : null; return `${config.drive_url}/${file._id}?thumbnail`;
} else { } else {
return `${config.drive_url}/${file._id}?web`; return `${config.drive_url}/${file._id}?web`;
} }

View file

@ -64,7 +64,12 @@ export default async function(ctx: Koa.BaseContext) {
const bucket = await getDriveFileThumbnailBucket(); const bucket = await getDriveFileThumbnailBucket();
ctx.body = bucket.openDownloadStream(thumb._id); ctx.body = bucket.openDownloadStream(thumb._id);
} else { } else {
if (file.contentType.startsWith('image/')) {
await sendRaw(); await sendRaw();
} else {
ctx.status = 404;
await send(ctx as any, '/dummy.png', { root: assets });
}
} }
} else if ('web' in ctx.query) { } else if ('web' in ctx.query) {
const web = await DriveFileWebpublic.findOne({ const web = await DriveFileWebpublic.findOne({

View file

@ -22,6 +22,7 @@ import { getDriveFileThumbnailBucket } from '../../models/drive-file-thumbnail';
import driveChart from '../../chart/drive'; import driveChart from '../../chart/drive';
import perUserDriveChart from '../../chart/per-user-drive'; import perUserDriveChart from '../../chart/per-user-drive';
import fetchMeta from '../../misc/fetch-meta'; import fetchMeta from '../../misc/fetch-meta';
import { GenerateVideoThumbnail } from './generate-video-thumbnail';
const log = debug('misskey:drive:add-file'); 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'; thumbnailExt = 'png';
thumbnailType = 'image/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 // #endregion thumbnail

View file

@ -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<Buffer> {
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;
}