diff --git a/server/libs/domain/src/media/media.repository.ts b/server/libs/domain/src/media/media.repository.ts index 83a4777ea3..9063ce5464 100644 --- a/server/libs/domain/src/media/media.repository.ts +++ b/server/libs/domain/src/media/media.repository.ts @@ -7,6 +7,6 @@ export interface ResizeOptions { export interface IMediaRepository { resize(input: string, output: string, options: ResizeOptions): Promise; - extractVideoThumbnail(input: string, output: string): Promise; + extractVideoThumbnail(input: string, output: string, size: number): Promise; extractThumbnailFromExif(input: string, output: string): Promise; } diff --git a/server/libs/domain/src/media/media.service.spec.ts b/server/libs/domain/src/media/media.service.spec.ts index 6de82faee3..332e444deb 100644 --- a/server/libs/domain/src/media/media.service.spec.ts +++ b/server/libs/domain/src/media/media.service.spec.ts @@ -114,6 +114,7 @@ describe(MediaService.name, () => { expect(mediaMock.extractVideoThumbnail).toHaveBeenCalledWith( '/original/path.ext', 'upload/thumbs/user-id/asset-id.jpeg', + 1440, ); expect(assetMock.save).toHaveBeenCalledWith({ id: 'asset-id', diff --git a/server/libs/domain/src/media/media.service.ts b/server/libs/domain/src/media/media.service.ts index 76b57a3353..e11c48a2c9 100644 --- a/server/libs/domain/src/media/media.service.ts +++ b/server/libs/domain/src/media/media.service.ts @@ -44,9 +44,13 @@ export class MediaService { this.storageRepository.mkdirSync(resizePath); const jpegThumbnailPath = join(resizePath, `${asset.id}.jpeg`); + const thumbnailDimension = 1440; if (asset.type == AssetType.IMAGE) { try { - await this.mediaRepository.resize(asset.originalPath, jpegThumbnailPath, { size: 1440, format: 'jpeg' }); + await this.mediaRepository.resize(asset.originalPath, jpegThumbnailPath, { + size: thumbnailDimension, + format: 'jpeg', + }); } catch (error) { this.logger.warn( `Failed to generate jpeg thumbnail using sharp, trying with exiftool-vendored (asset=${asset.id})`, @@ -57,7 +61,7 @@ export class MediaService { if (asset.type == AssetType.VIDEO) { this.logger.log('Start Generating Video Thumbnail'); - await this.mediaRepository.extractVideoThumbnail(asset.originalPath, jpegThumbnailPath); + await this.mediaRepository.extractVideoThumbnail(asset.originalPath, jpegThumbnailPath, thumbnailDimension); this.logger.log(`Generating Video Thumbnail Success ${asset.id}`); } diff --git a/server/libs/infra/src/repositories/media.repository.ts b/server/libs/infra/src/repositories/media.repository.ts index 779ccbc0c6..afd3ca0a5b 100644 --- a/server/libs/infra/src/repositories/media.repository.ts +++ b/server/libs/infra/src/repositories/media.repository.ts @@ -11,7 +11,11 @@ export class MediaRepository implements IMediaRepository { async resize(input: string, output: string, options: ResizeOptions): Promise { switch (options.format) { case 'webp': - await sharp(input, { failOnError: false }).resize(250).webp().rotate().toFile(output); + await sharp(input, { failOnError: false }) + .resize(options.size, options.size, { fit: 'outside', withoutEnlargement: true }) + .webp() + .rotate() + .toFile(output); return; case 'jpeg': @@ -24,10 +28,14 @@ export class MediaRepository implements IMediaRepository { } } - extractVideoThumbnail(input: string, output: string) { + extractVideoThumbnail(input: string, output: string, size: number) { return new Promise((resolve, reject) => { ffmpeg(input) - .outputOptions(['-ss 00:00:00.000', '-frames:v 1']) + .outputOptions([ + '-ss 00:00:00.000', + '-frames:v 1', + `-vf scale='min(${size},iw)':'min(${size},ih)':force_original_aspect_ratio=increase`, + ]) .output(output) .on('error', reject) .on('end', resolve)