diff --git a/server/src/services/media.service.spec.ts b/server/src/services/media.service.spec.ts index 6f02e72253..e58dc1d013 100644 --- a/server/src/services/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -251,6 +251,17 @@ describe(MediaService.name, () => { expect(assetMock.update).toHaveBeenCalledWith({ id: 'asset-id', previewPath }); }); + it('should delete previous preview if different path', async () => { + const previousPreviewPath = assetStub.image.previewPath; + + configMock.load.mockResolvedValue([{ key: SystemConfigKey.IMAGE_THUMBNAIL_FORMAT, value: ImageFormat.WEBP }]); + assetMock.getByIds.mockResolvedValue([assetStub.image]); + + await sut.handleGeneratePreview({ id: assetStub.image.id }); + + expect(storageMock.unlink).toHaveBeenCalledWith(previousPreviewPath); + }); + it('should generate a P3 thumbnail for a wide gamut image', async () => { assetMock.getByIds.mockResolvedValue([ { ...assetStub.image, exifInfo: { profileDescription: 'Adobe RGB', bitsPerSample: 14 } as ExifEntity }, @@ -390,6 +401,17 @@ describe(MediaService.name, () => { expect(assetMock.update).toHaveBeenCalledWith({ id: 'asset-id', thumbnailPath }); }, ); + + it('should delete previous thumbnail if different path', async () => { + const previousThumbnailPath = assetStub.image.thumbnailPath; + + configMock.load.mockResolvedValue([{ key: SystemConfigKey.IMAGE_THUMBNAIL_FORMAT, value: ImageFormat.WEBP }]); + assetMock.getByIds.mockResolvedValue([assetStub.image]); + + await sut.handleGenerateThumbnail({ id: assetStub.image.id }); + + expect(storageMock.unlink).toHaveBeenCalledWith(previousThumbnailPath); + }); }); it('should generate a P3 thumbnail for a wide gamut image', async () => { diff --git a/server/src/services/media.service.ts b/server/src/services/media.service.ts index 1795db86d0..908c64ca97 100644 --- a/server/src/services/media.service.ts +++ b/server/src/services/media.service.ts @@ -185,6 +185,10 @@ export class MediaService { } const previewPath = await this.generateThumbnail(asset, AssetPathType.PREVIEW, image.previewFormat); + if (asset.previewPath && asset.previewPath !== previewPath) { + this.logger.debug(`Deleting old preview for asset ${asset.id}`); + await this.storageRepository.unlink(asset.previewPath); + } await this.assetRepository.update({ id: asset.id, previewPath }); return JobStatus.SUCCESS; } @@ -253,6 +257,10 @@ export class MediaService { } const thumbnailPath = await this.generateThumbnail(asset, AssetPathType.THUMBNAIL, image.thumbnailFormat); + if (asset.thumbnailPath && asset.thumbnailPath !== thumbnailPath) { + this.logger.debug(`Deleting old thumbnail for asset ${asset.id}`); + await this.storageRepository.unlink(asset.thumbnailPath); + } await this.assetRepository.update({ id: asset.id, thumbnailPath }); return JobStatus.SUCCESS; } diff --git a/server/test/fixtures/asset.stub.ts b/server/test/fixtures/asset.stub.ts index c6fe89d6fe..9bd3edbaaf 100644 --- a/server/test/fixtures/asset.stub.ts +++ b/server/test/fixtures/asset.stub.ts @@ -471,6 +471,24 @@ export const assetStub = { }, } as AssetEntity), + liveMotionWithThumb: Object.freeze({ + id: fileStub.livePhotoMotion.uuid, + originalPath: fileStub.livePhotoMotion.originalPath, + ownerId: authStub.user1.user.id, + type: AssetType.VIDEO, + isVisible: false, + fileModifiedAt: new Date('2022-06-19T23:41:36.910Z'), + fileCreatedAt: new Date('2022-06-19T23:41:36.910Z'), + libraryId: 'library-id', + library: libraryStub.uploadLibrary1, + previewPath: '/uploads/user-id/thumbs/path.ext', + thumbnailPath: '/uploads/user-id/webp/path.ext', + exifInfo: { + fileSizeInByte: 100_000, + timeZone: `America/New_York`, + }, + } as AssetEntity), + livePhotoStillAsset: Object.freeze({ id: 'live-photo-still-asset', originalPath: fileStub.livePhotoStill.originalPath,