Generate video thumbnails (#4084)
* Generate video thumbnails * import order
This commit is contained in:
		
							parent
							
								
									170b1bb4cc
								
							
						
					
					
						commit
						3040700005
					
				
					 7 changed files with 51 additions and 4 deletions
				
			
		|  | @ -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", | ||||||
|  |  | ||||||
|  | @ -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})` | ||||||
| 			}; | 			}; | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -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})` | ||||||
| 			}; | 			}; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -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`; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -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 { | ||||||
| 			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) { | 	} else if ('web' in ctx.query) { | ||||||
| 		const web = await DriveFileWebpublic.findOne({ | 		const web = await DriveFileWebpublic.findOne({ | ||||||
|  |  | ||||||
|  | @ -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
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										32
									
								
								src/services/drive/generate-video-thumbnail.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/services/drive/generate-video-thumbnail.ts
									
										
									
									
									
										Normal 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; | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue