aboutsummaryrefslogtreecommitdiff
path: root/src/api/utils/ThumbUtil.js
blob: fb6e47fa6fcb8ecc3c36430192797a412a0a76a9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
const jetpack = require('fs-jetpack');
const path = require('path');
const sharp = require('sharp');
const ffmpeg = require('fluent-ffmpeg');
const previewUtil = require('./videoPreview/FragmentPreview');

const log = require('./Log');

class ThumbUtil {
	static imageExtensions = ['.jpg', '.jpeg', '.gif', '.png', '.webp'];
	static videoExtensions = ['.webm', '.mp4', '.wmv', '.avi', '.mov'];

	static thumbPath = path.join(__dirname, '../../../', 'uploads', 'thumbs');
	static squareThumbPath = path.join(__dirname, '../../../', 'uploads', 'thumbs', 'square');
	static videoPreviewPath = path.join(__dirname, '../../../', 'uploads', 'thumbs', 'preview');

	static generateThumbnails(filename) {
		if (!filename) return;
		const ext = path.extname(filename).toLowerCase();
		const output = `${filename.slice(0, -ext.length)}.webp`;
		const previewOutput = `${filename.slice(0, -ext.length)}.webm`;

		// eslint-disable-next-line max-len
		if (ThumbUtil.imageExtensions.includes(ext)) return ThumbUtil.generateThumbnailForImage(filename, output);
		// eslint-disable-next-line max-len
		if (ThumbUtil.videoExtensions.includes(ext)) return ThumbUtil.generateThumbnailForVideo(filename, previewOutput);
		return null;
	}

	static async generateThumbnailForImage(filename, output) {
		const filePath = path.join(__dirname, '../../../', 'uploads', filename);

		const file = await jetpack.readAsync(filePath, 'buffer');
		await sharp(file)
			.resize(64, 64)
			.toFormat('webp')
			.toFile(path.join(ThumbUtil.squareThumbPath, output));
		await sharp(file)
			.resize(225, null)
			.toFormat('webp')
			.toFile(path.join(ThumbUtil.thumbPath, output));
	}

	static async generateThumbnailForVideo(filename, output) {
		const filePath = path.join(__dirname, '../../../', 'uploads', filename);

		ffmpeg(filePath)
			.thumbnail({
				timestamps: [0],
				filename: '%b.webp',
				folder: ThumbUtil.squareThumbPath,
				size: '64x64'
			})
			.on('error', error => log.error(error.message));

		ffmpeg(filePath)
			.thumbnail({
				timestamps: [0],
				filename: '%b.webp',
				folder: ThumbUtil.thumbPath,
				size: '150x?'
			})
			.on('error', error => log.error(error.message));

		try {
			await previewUtil({
				input: filePath,
				width: 150,
				output: path.join(ThumbUtil.videoPreviewPath, output)
			});
		} catch (e) {
			log.error(e);
		}
	}

	static getFileThumbnail(filename) {
		if (!filename) return null;
		const ext = path.extname(filename).toLowerCase();

		const isImage = ThumbUtil.imageExtensions.includes(ext);
		const isVideo = ThumbUtil.videoExtensions.includes(ext);

		if (isImage) return { thumb: `${filename.slice(0, -ext.length)}.webp` };
		if (isVideo) {
			return {
				thumb: `${filename.slice(0, -ext.length)}.webp`,
				preview: `${filename.slice(0, -ext.length)}.webm`
			};
		}

		return null;
	}

	static async removeThumbs({ thumb, preview }) {
		if (thumb) {
			await jetpack.removeAsync(path.join(ThumbUtil.thumbPath, thumb));
			await jetpack.removeAsync(path.join(ThumbUtil.squareThumbPath, thumb));
		}
		if (preview) {
			await jetpack.removeAsync(path.join(ThumbUtil.videoPreviewPath, preview));
		}
	}
}

module.exports = ThumbUtil;