Add webp thumbnail conversion task to optimize performance of fast scrolling (#172)

* Update readme

* Added webp to table and entity

* Added cronjob and sharp dependencies

* Added conversion of webp every 5 minutes and endpoint will now server webp image if exist
This commit is contained in:
Alex
2022-05-22 06:56:36 -05:00
committed by GitHub
parent ce06af0c9b
commit 55c5027539
11 changed files with 731 additions and 21 deletions

View File

@@ -114,7 +114,11 @@ export class AssetService {
public async getAssetThumbnail(assetId: string) {
const asset = await this.assetRepository.findOne({ id: assetId });
return new StreamableFile(createReadStream(asset.resizePath));
if (asset.webpPath != '') {
return new StreamableFile(createReadStream(asset.webpPath));
} else {
return new StreamableFile(createReadStream(asset.resizePath));
}
}
public async serveFile(authUser: AuthUserDto, query: ServeFileDto, res: Res, headers: any) {
@@ -132,7 +136,11 @@ export class AssetService {
if (query.isThumb === 'false' || !query.isThumb) {
file = createReadStream(asset.originalPath);
} else {
file = createReadStream(asset.resizePath);
if (asset.webpPath != '') {
file = createReadStream(asset.webpPath);
} else {
file = createReadStream(asset.resizePath);
}
}
file.on('error', (error) => {

View File

@@ -26,6 +26,9 @@ export class AssetEntity {
@Column({ nullable: true })
resizePath: string;
@Column({ nullable: true })
webpPath: string;
@Column()
createdAt: string;

View File

@@ -16,16 +16,26 @@ import { BackgroundTaskModule } from './modules/background-task/background-task.
import { CommunicationModule } from './api-v1/communication/communication.module';
import { SharingModule } from './api-v1/sharing/sharing.module';
import { AppController } from './app.controller';
import { ScheduleModule } from '@nestjs/schedule';
import { ScheduleTasksModule } from './modules/schedule-tasks/schedule-tasks.module';
@Module({
imports: [
ConfigModule.forRoot(immichAppConfig),
TypeOrmModule.forRoot(databaseConfig),
UserModule,
AssetModule,
AuthModule,
ImmichJwtModule,
DeviceInfoModule,
BullModule.forRootAsync({
useFactory: async () => ({
redis: {
@@ -44,6 +54,10 @@ import { AppController } from './app.controller';
CommunicationModule,
SharingModule,
ScheduleModule.forRoot(),
ScheduleTasksModule
],
controllers: [AppController],
providers: [],

View File

@@ -17,5 +17,6 @@ export const immichAppConfig: ConfigModuleOptions = {
then: Joi.string().optional().allow(null, ''),
otherwise: Joi.string().required(),
}),
VITE_SERVER_ENDPOINT: Joi.string().required(),
}),
};

View File

@@ -3,7 +3,7 @@
export const serverVersion = {
major: 1,
minor: 9,
minor: 10,
patch: 0,
build: 13,
build: 14,
};

View File

@@ -0,0 +1,19 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class UpdateAssetTableWithWebpPath1653214255670 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
alter table assets
add column if not exists "webpPath" varchar default '';
`)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
alter table assets
drop column if exists "webpPath";
`);
}
}

View File

@@ -0,0 +1,52 @@
import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { AssetEntity } from '../../api-v1/asset/entities/asset.entity';
import sharp from 'sharp';
@Injectable()
export class ImageConversionService {
constructor(
@InjectRepository(AssetEntity)
private assetRepository: Repository<AssetEntity>
) { }
@Cron(CronExpression.EVERY_5_MINUTES
, {
name: 'webp-conversion'
})
async webpConversion() {
Logger.log('Starting Webp Conversion Tasks', 'ImageConversionService')
const assets = await this.assetRepository.find({
where: {
webpPath: ''
},
take: 500
});
if (assets.length == 0) {
Logger.log('All assets has webp file - aborting task', 'ImageConversionService')
return;
}
for (const asset of assets) {
const resizePath = asset.resizePath;
if (resizePath != '') {
const webpPath = resizePath.replace('jpeg', 'webp')
sharp(resizePath).resize(250).webp().toFile(webpPath, (err, info) => {
if (!err) {
this.assetRepository.update({ id: asset.id }, { webpPath: webpPath })
}
});
}
}
}
}

View File

@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AssetEntity } from '../../api-v1/asset/entities/asset.entity';
import { ImageConversionService } from './image-conversion.service';
@Module({
imports: [
TypeOrmModule.forFeature([AssetEntity]),
],
providers: [ImageConversionService],
})
export class ScheduleTasksModule { }