2022-06-19 08:16:35 -05:00
|
|
|
import { InjectQueue, Process, Processor } from '@nestjs/bull';
|
|
|
|
import { Job, Queue } from 'bull';
|
|
|
|
import { AssetEntity, AssetType } from '@app/database/entities/asset.entity';
|
2022-06-11 16:12:06 -05:00
|
|
|
import { Repository } from 'typeorm/repository/Repository';
|
|
|
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
|
|
import sharp from 'sharp';
|
2022-06-19 08:16:35 -05:00
|
|
|
import { existsSync, mkdirSync } from 'node:fs';
|
|
|
|
import { randomUUID } from 'node:crypto';
|
|
|
|
import { CommunicationGateway } from '../../../immich/src/api-v1/communication/communication.gateway';
|
|
|
|
import ffmpeg from 'fluent-ffmpeg';
|
|
|
|
import { Logger } from '@nestjs/common';
|
2022-06-11 16:12:06 -05:00
|
|
|
|
|
|
|
@Processor('thumbnail-generator-queue')
|
|
|
|
export class ThumbnailGeneratorProcessor {
|
|
|
|
constructor(
|
|
|
|
@InjectRepository(AssetEntity)
|
|
|
|
private assetRepository: Repository<AssetEntity>,
|
2022-06-19 08:16:35 -05:00
|
|
|
|
|
|
|
@InjectQueue('thumbnail-generator-queue')
|
|
|
|
private thumbnailGeneratorQueue: Queue,
|
|
|
|
|
|
|
|
private wsCommunicateionGateway: CommunicationGateway,
|
2022-06-21 18:00:30 -05:00
|
|
|
|
|
|
|
@InjectQueue('metadata-extraction-queue')
|
|
|
|
private metadataExtractionQueue: Queue,
|
2022-06-11 16:12:06 -05:00
|
|
|
) {}
|
|
|
|
|
|
|
|
@Process('generate-jpeg-thumbnail')
|
|
|
|
async generateJPEGThumbnail(job: Job) {
|
|
|
|
const { asset }: { asset: AssetEntity } = job.data;
|
|
|
|
|
2022-06-19 08:16:35 -05:00
|
|
|
const resizePath = `upload/${asset.userId}/thumb/${asset.deviceId}/`;
|
|
|
|
|
|
|
|
if (!existsSync(resizePath)) {
|
|
|
|
mkdirSync(resizePath, { recursive: true });
|
|
|
|
}
|
|
|
|
|
|
|
|
const temp = asset.originalPath.split('/');
|
|
|
|
const originalFilename = temp[temp.length - 1].split('.')[0];
|
|
|
|
const jpegThumbnailPath = resizePath + originalFilename + '.jpeg';
|
|
|
|
|
|
|
|
if (asset.type == AssetType.IMAGE) {
|
|
|
|
sharp(asset.originalPath)
|
|
|
|
.resize(1440, 2560, { fit: 'inside' })
|
|
|
|
.jpeg()
|
2022-06-25 19:53:06 +02:00
|
|
|
.toFile(jpegThumbnailPath, async (err) => {
|
2022-06-19 08:16:35 -05:00
|
|
|
if (!err) {
|
|
|
|
await this.assetRepository.update({ id: asset.id }, { resizePath: jpegThumbnailPath });
|
|
|
|
|
|
|
|
// Update resize path to send to generate webp queue
|
|
|
|
asset.resizePath = jpegThumbnailPath;
|
|
|
|
|
|
|
|
await this.thumbnailGeneratorQueue.add('generate-webp-thumbnail', { asset }, { jobId: randomUUID() });
|
2022-06-21 18:00:30 -05:00
|
|
|
await this.metadataExtractionQueue.add('tag-image', { asset }, { jobId: randomUUID() });
|
|
|
|
await this.metadataExtractionQueue.add('detect-object', { asset }, { jobId: randomUUID() });
|
2022-06-19 08:16:35 -05:00
|
|
|
this.wsCommunicateionGateway.server.to(asset.userId).emit('on_upload_success', JSON.stringify(asset));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset.type == AssetType.VIDEO) {
|
|
|
|
ffmpeg(asset.originalPath)
|
2022-07-01 12:00:12 -05:00
|
|
|
.outputOptions(['-ss 00:00:00.000', '-frames:v 1'])
|
2022-06-19 08:16:35 -05:00
|
|
|
.output(jpegThumbnailPath)
|
|
|
|
.on('start', () => {
|
|
|
|
Logger.log('Start Generating Video Thumbnail', 'generateJPEGThumbnail');
|
|
|
|
})
|
2022-06-25 19:53:06 +02:00
|
|
|
.on('error', (error) => {
|
2022-06-19 08:16:35 -05:00
|
|
|
Logger.error(`Cannot Generate Video Thumbnail ${error}`, 'generateJPEGThumbnail');
|
|
|
|
// reject();
|
|
|
|
})
|
|
|
|
.on('end', async () => {
|
|
|
|
Logger.log(`Generating Video Thumbnail Success ${asset.id}`, 'generateJPEGThumbnail');
|
|
|
|
await this.assetRepository.update({ id: asset.id }, { resizePath: jpegThumbnailPath });
|
|
|
|
|
|
|
|
// Update resize path to send to generate webp queue
|
|
|
|
asset.resizePath = jpegThumbnailPath;
|
|
|
|
|
|
|
|
await this.thumbnailGeneratorQueue.add('generate-webp-thumbnail', { asset }, { jobId: randomUUID() });
|
2022-06-21 18:00:30 -05:00
|
|
|
await this.metadataExtractionQueue.add('tag-image', { asset }, { jobId: randomUUID() });
|
|
|
|
await this.metadataExtractionQueue.add('detect-object', { asset }, { jobId: randomUUID() });
|
2022-06-19 08:16:35 -05:00
|
|
|
|
|
|
|
this.wsCommunicateionGateway.server.to(asset.userId).emit('on_upload_success', JSON.stringify(asset));
|
|
|
|
})
|
|
|
|
.run();
|
|
|
|
}
|
2022-06-11 16:12:06 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@Process({ name: 'generate-webp-thumbnail', concurrency: 2 })
|
2022-06-25 19:53:06 +02:00
|
|
|
async generateWepbThumbnail(job: Job<{ asset: AssetEntity }>) {
|
|
|
|
const { asset } = job.data;
|
2022-06-11 16:12:06 -05:00
|
|
|
|
2022-06-25 19:53:06 +02:00
|
|
|
if (!asset.resizePath) {
|
|
|
|
return;
|
|
|
|
}
|
2022-06-11 16:12:06 -05:00
|
|
|
const webpPath = asset.resizePath.replace('jpeg', 'webp');
|
|
|
|
|
|
|
|
sharp(asset.resizePath)
|
|
|
|
.resize(250)
|
|
|
|
.webp()
|
2022-06-25 19:53:06 +02:00
|
|
|
.toFile(webpPath, (err) => {
|
2022-06-11 16:12:06 -05:00
|
|
|
if (!err) {
|
|
|
|
this.assetRepository.update({ id: asset.id }, { webpPath: webpPath });
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|