mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	feat(web,server): add thumbhash support (#2649)
* add thumbhash: server generation and web impl * move logic to infra & use byta in db * remove unnecesary logs * update generated API and simplify thumbhash gen * fix check errors * removed unnecessary library and css tag * style edits * syntax mistake * update server test, change thumbhash job name * fix tests * Update server/src/domain/asset/response-dto/asset-response.dto.ts Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com> * add unit test, change migration date * change to official thumbhash impl * update call method to not use eval * "generate missing" looks for thumbhash * improve queue & improve syntax * update syntax again * update tests * fix thumbhash generation * consolidate queueing to avoid duplication * cover all types of incorrect thumbnail cases * split out jest tasks * put back thumbnail duration loading for images without thumbhash * Remove stray package.json --------- Co-authored-by: Luke McCarthy <mail@lukehmcc.com> Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com> Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
		@@ -196,7 +196,8 @@ export const assetEntityStub = {
 | 
			
		||||
    resizePath: null,
 | 
			
		||||
    checksum: Buffer.from('file hash', 'utf8'),
 | 
			
		||||
    type: AssetType.IMAGE,
 | 
			
		||||
    webpPath: null,
 | 
			
		||||
    webpPath: '/uploads/user-id/webp/path.ext',
 | 
			
		||||
    thumbhash: Buffer.from('blablabla', 'base64'),
 | 
			
		||||
    encodedVideoPath: null,
 | 
			
		||||
    createdAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
    updatedAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
@@ -212,7 +213,7 @@ export const assetEntityStub = {
 | 
			
		||||
    faces: [],
 | 
			
		||||
    sidecarPath: null,
 | 
			
		||||
  }),
 | 
			
		||||
  image: Object.freeze<AssetEntity>({
 | 
			
		||||
  noWebpPath: Object.freeze<AssetEntity>({
 | 
			
		||||
    id: 'asset-id',
 | 
			
		||||
    deviceAssetId: 'device-asset-id',
 | 
			
		||||
    fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
@@ -225,6 +226,67 @@ export const assetEntityStub = {
 | 
			
		||||
    checksum: Buffer.from('file hash', 'utf8'),
 | 
			
		||||
    type: AssetType.IMAGE,
 | 
			
		||||
    webpPath: null,
 | 
			
		||||
    thumbhash: Buffer.from('blablabla', 'base64'),
 | 
			
		||||
    encodedVideoPath: null,
 | 
			
		||||
    createdAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
    updatedAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
    mimeType: null,
 | 
			
		||||
    isFavorite: true,
 | 
			
		||||
    isArchived: false,
 | 
			
		||||
    duration: null,
 | 
			
		||||
    isVisible: true,
 | 
			
		||||
    livePhotoVideo: null,
 | 
			
		||||
    livePhotoVideoId: null,
 | 
			
		||||
    tags: [],
 | 
			
		||||
    sharedLinks: [],
 | 
			
		||||
    originalFileName: 'asset-id.ext',
 | 
			
		||||
    faces: [],
 | 
			
		||||
    sidecarPath: null,
 | 
			
		||||
  }),
 | 
			
		||||
  noThumbhash: Object.freeze<AssetEntity>({
 | 
			
		||||
    id: 'asset-id',
 | 
			
		||||
    deviceAssetId: 'device-asset-id',
 | 
			
		||||
    fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
    fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
    owner: userEntityStub.user1,
 | 
			
		||||
    ownerId: 'user-id',
 | 
			
		||||
    deviceId: 'device-id',
 | 
			
		||||
    originalPath: '/original/path.ext',
 | 
			
		||||
    resizePath: '/uploads/user-id/thumbs/path.ext',
 | 
			
		||||
    checksum: Buffer.from('file hash', 'utf8'),
 | 
			
		||||
    type: AssetType.IMAGE,
 | 
			
		||||
    webpPath: '/uploads/user-id/webp/path.ext',
 | 
			
		||||
    thumbhash: null,
 | 
			
		||||
    encodedVideoPath: null,
 | 
			
		||||
    createdAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
    updatedAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
    mimeType: null,
 | 
			
		||||
    isFavorite: true,
 | 
			
		||||
    isArchived: false,
 | 
			
		||||
    duration: null,
 | 
			
		||||
    isVisible: true,
 | 
			
		||||
    livePhotoVideo: null,
 | 
			
		||||
    livePhotoVideoId: null,
 | 
			
		||||
    tags: [],
 | 
			
		||||
    sharedLinks: [],
 | 
			
		||||
    originalFileName: 'asset-id.ext',
 | 
			
		||||
    faces: [],
 | 
			
		||||
    sidecarPath: null,
 | 
			
		||||
  }),
 | 
			
		||||
  image: Object.freeze<AssetEntity>({
 | 
			
		||||
    id: 'asset-id',
 | 
			
		||||
    deviceAssetId: 'device-asset-id',
 | 
			
		||||
    fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
    fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
    owner: userEntityStub.user1,
 | 
			
		||||
    ownerId: 'user-id',
 | 
			
		||||
    deviceId: 'device-id',
 | 
			
		||||
    originalPath: '/original/path.ext',
 | 
			
		||||
    resizePath: '/uploads/user-id/thumbs/path.ext',
 | 
			
		||||
    checksum: Buffer.from('file hash', 'utf8'),
 | 
			
		||||
    type: AssetType.IMAGE,
 | 
			
		||||
    webpPath: '/uploads/user-id/webp/path.ext',
 | 
			
		||||
    thumbhash: Buffer.from('blablabla', 'base64'),
 | 
			
		||||
    encodedVideoPath: null,
 | 
			
		||||
    createdAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
    updatedAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
@@ -255,6 +317,7 @@ export const assetEntityStub = {
 | 
			
		||||
    checksum: Buffer.from('file hash', 'utf8'),
 | 
			
		||||
    type: AssetType.VIDEO,
 | 
			
		||||
    webpPath: null,
 | 
			
		||||
    thumbhash: null,
 | 
			
		||||
    encodedVideoPath: null,
 | 
			
		||||
    createdAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
    updatedAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
@@ -305,6 +368,7 @@ export const assetEntityStub = {
 | 
			
		||||
    sidecarPath: null,
 | 
			
		||||
    type: AssetType.IMAGE,
 | 
			
		||||
    webpPath: null,
 | 
			
		||||
    thumbhash: null,
 | 
			
		||||
    encodedVideoPath: null,
 | 
			
		||||
    createdAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
    updatedAt: new Date('2023-02-23T05:06:29.716Z'),
 | 
			
		||||
@@ -334,6 +398,7 @@ export const assetEntityStub = {
 | 
			
		||||
    deviceId: 'device-id',
 | 
			
		||||
    originalPath: '/original/path.ext',
 | 
			
		||||
    resizePath: '/uploads/user-id/thumbs/path.ext',
 | 
			
		||||
    thumbhash: null,
 | 
			
		||||
    checksum: Buffer.from('file hash', 'utf8'),
 | 
			
		||||
    type: AssetType.IMAGE,
 | 
			
		||||
    webpPath: null,
 | 
			
		||||
@@ -507,6 +572,7 @@ const assetResponse: AssetResponseDto = {
 | 
			
		||||
  originalPath: 'fake_path/jpeg',
 | 
			
		||||
  originalFileName: 'asset_1.jpeg',
 | 
			
		||||
  resized: false,
 | 
			
		||||
  thumbhash: null,
 | 
			
		||||
  fileModifiedAt: today,
 | 
			
		||||
  fileCreatedAt: today,
 | 
			
		||||
  updatedAt: today,
 | 
			
		||||
@@ -787,6 +853,7 @@ export const sharedLinkStub = {
 | 
			
		||||
            clipEmbedding: [0.12, 0.13, 0.14],
 | 
			
		||||
          },
 | 
			
		||||
          webpPath: '',
 | 
			
		||||
          thumbhash: null,
 | 
			
		||||
          encodedVideoPath: '',
 | 
			
		||||
          duration: null,
 | 
			
		||||
          isVisible: true,
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ import { IMediaRepository } from '@app/domain';
 | 
			
		||||
export const newMediaRepositoryMock = (): jest.Mocked<IMediaRepository> => {
 | 
			
		||||
  return {
 | 
			
		||||
    extractVideoThumbnail: jest.fn(),
 | 
			
		||||
    generateThumbhash: jest.fn(),
 | 
			
		||||
    resize: jest.fn(),
 | 
			
		||||
    crop: jest.fn(),
 | 
			
		||||
    probe: jest.fn(),
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user