1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2024-12-28 22:51:59 +00:00
This commit is contained in:
Sese Schneider 2024-12-27 09:18:56 +01:00
parent 2315466be3
commit 48bf55dad8
No known key found for this signature in database
GPG key ID: 1414BDF9A6FA09A9
5 changed files with 104 additions and 111 deletions

View file

@ -105,7 +105,8 @@
{/if} {/if}
{/await} {/await}
- -
{getExifCount(asset)} {$t('exif')} {getExifCount(asset)}
{$t('exif')}
</span> </span>
</div> </div>
</div> </div>

View file

@ -1,45 +1,37 @@
import { suggestDuplicate } from '$lib/utils/duplicate-utils'; import { suggestDuplicate } from '$lib/utils/duplicate-utils';
import type { AssetResponseDto } from '@immich/sdk'; import type { AssetResponseDto } from '@immich/sdk';
describe('choosing a duplicate', () => { describe('choosing a duplicate', () => {
it('picks the asset with the largest file size', () => {
it('picks the asset with the largest file size', () => { const assets = [
const assets = [ { exifInfo: { fileSizeInByte: 300 } },
{ exifInfo: { fileSizeInByte: 300 } }, { exifInfo: { fileSizeInByte: 200 } },
{ exifInfo: { fileSizeInByte: 200 } }, { exifInfo: { fileSizeInByte: 100 } },
{ exifInfo: { fileSizeInByte: 100 } }, ];
]; expect(suggestDuplicate(assets as AssetResponseDto[])).toEqual(assets[0]);
expect(suggestDuplicate(assets as AssetResponseDto[])).toEqual(assets[0]); });
});
it('picks the asset with the most exif data if multiple assets have the same file size', () => {
it('picks the asset with the most exif data if multiple assets have the same file size', () => { const assets = [
const assets = [ { exifInfo: { fileSizeInByte: 200, rating: 5, fNumber: 1 } },
{ exifInfo: { fileSizeInByte: 200, rating: 5, fNumber: 1 } }, { exifInfo: { fileSizeInByte: 200, rating: 5 } },
{ exifInfo: { fileSizeInByte: 200, rating: 5 } }, { exifInfo: { fileSizeInByte: 100, rating: 5 } },
{ exifInfo: { fileSizeInByte: 100, rating: 5 } }, ];
]; expect(suggestDuplicate(assets as AssetResponseDto[])).toEqual(assets[0]);
expect(suggestDuplicate(assets as AssetResponseDto[])).toEqual(assets[0]); });
});
it('returns undefined for an empty array', () => {
it('returns undefined for an empty array', () => { const assets: AssetResponseDto[] = [];
const assets: AssetResponseDto[] = []; expect(suggestDuplicate(assets)).toBeUndefined();
expect(suggestDuplicate(assets)).toBeUndefined(); });
});
it('handles assets with no exifInfo', () => {
it('handles assets with no exifInfo', () => { const assets = [{ exifInfo: { fileSizeInByte: 200 } }, {}];
const assets = [ expect(suggestDuplicate(assets as AssetResponseDto[])).toEqual(assets[0]);
{ exifInfo: { fileSizeInByte: 200 } }, });
{},
]; it('handles assets with exifInfo but no fileSizeInByte', () => {
expect(suggestDuplicate(assets as AssetResponseDto[])).toEqual(assets[0]); const assets = [{ exifInfo: { rating: 5, fNumber: 1 } }, { exifInfo: { rating: 5 } }];
}); expect(suggestDuplicate(assets as AssetResponseDto[])).toEqual(assets[0]);
});
it('handles assets with exifInfo but no fileSizeInByte', () => { });
const assets = [
{ exifInfo: { rating: 5, fNumber: 1 } },
{ exifInfo: { rating: 5 } },
];
expect(suggestDuplicate(assets as AssetResponseDto[])).toEqual(assets[0]);
});
});

View file

@ -1,29 +1,31 @@
import type { AssetResponseDto } from '@immich/sdk'; import { getExifCount } from '$lib/utils/exif-utils';
import { sortBy } from 'lodash-es'; import type { AssetResponseDto } from '@immich/sdk';
import { getExifCount } from '$lib/utils/exif-utils'; import { sortBy } from 'lodash-es';
/** /**
* Suggests the best duplicate asset to keep from a list of duplicates. * Suggests the best duplicate asset to keep from a list of duplicates.
* *
* The best asset is determined by the following criteria: * The best asset is determined by the following criteria:
* - The asset with the largest file size * - The asset with the largest file size
* - If there are multiple assets with the same file size, the asset with the most exif data * - If there are multiple assets with the same file size, the asset with the most exif data
* *
* @param assets List of duplicate assets * @param assets List of duplicate assets
* @returns The best asset to keep * @returns The best asset to keep
*/ */
export const suggestDuplicate = (assets: AssetResponseDto[]): AssetResponseDto | undefined => { export const suggestDuplicate = (assets: AssetResponseDto[]): AssetResponseDto | undefined => {
const assetsBySize = sortBy(assets, (asset) => asset.exifInfo?.fileSizeInByte ?? 0); const assetsBySize = sortBy(assets, (asset) => asset.exifInfo?.fileSizeInByte ?? 0);
// All assets with the same file size as the largest asset // All assets with the same file size as the largest asset
const highestSizeAssets = assetsBySize.filter((asset) => asset.exifInfo?.fileSizeInByte === assetsBySize.at(-1)?.exifInfo?.fileSizeInByte); const highestSizeAssets = assetsBySize.filter(
(asset) => asset.exifInfo?.fileSizeInByte === assetsBySize.at(-1)?.exifInfo?.fileSizeInByte,
// If there are multiple assets with the same file size, return the one with the most exif data );
if(highestSizeAssets.length >= 2) {
const assetsByExifCount = sortBy(highestSizeAssets, getExifCount); // If there are multiple assets with the same file size, return the one with the most exif data
return assetsByExifCount.pop(); if (highestSizeAssets.length >= 2) {
} const assetsByExifCount = sortBy(highestSizeAssets, getExifCount);
return assetsByExifCount.pop();
// Return the asset with the largest file size }
return assetsBySize.pop();
}; // Return the asset with the largest file size
return assetsBySize.pop();
};

View file

@ -1,31 +1,29 @@
import { getExifCount } from '$lib/utils/exif-utils'; import { getExifCount } from '$lib/utils/exif-utils';
import type { AssetResponseDto } from '@immich/sdk'; import type { AssetResponseDto } from '@immich/sdk';
describe('getting the exif count', () => { describe('getting the exif count', () => {
it('returns 0 when exifInfo is undefined', () => {
it('returns 0 when exifInfo is undefined', () => { const asset = {};
const asset = {}; expect(getExifCount(asset as AssetResponseDto)).toBe(0);
expect(getExifCount(asset as AssetResponseDto)).toBe(0); });
});
it('returns 0 when exifInfo is empty', () => {
it('returns 0 when exifInfo is empty', () => { const asset = { exifInfo: {} };
const asset = { exifInfo: {} }; expect(getExifCount(asset as AssetResponseDto)).toBe(0);
expect(getExifCount(asset as AssetResponseDto)).toBe(0); });
});
it('returns the correct count of non-null exifInfo properties', () => {
it('returns the correct count of non-null exifInfo properties', () => { const asset = { exifInfo: { fileSizeInByte: 200, rating: 5, fNumber: null } };
const asset = { exifInfo: { fileSizeInByte: 200, rating: 5, fNumber: null } }; expect(getExifCount(asset as AssetResponseDto)).toBe(2);
expect(getExifCount(asset as AssetResponseDto)).toBe(2); });
});
it('ignores null, undefined and empty properties in exifInfo', () => {
it('ignores null, undefined and empty properties in exifInfo', () => { const asset = { exifInfo: { fileSizeInByte: 200, rating: null, fNumber: undefined, description: '' } };
const asset = { exifInfo: { fileSizeInByte: 200, rating: null, fNumber: undefined, description: '' } }; expect(getExifCount(asset as AssetResponseDto)).toBe(1);
expect(getExifCount(asset as AssetResponseDto)).toBe(1); });
});
it('returns the correct count when all exifInfo properties are non-null', () => {
it('returns the correct count when all exifInfo properties are non-null', () => { const asset = { exifInfo: { fileSizeInByte: 200, rating: 5, fNumber: 1, description: 'test' } };
const asset = { exifInfo: { fileSizeInByte: 200, rating: 5, fNumber: 1, description: 'test' } }; expect(getExifCount(asset as AssetResponseDto)).toBe(4);
expect(getExifCount(asset as AssetResponseDto)).toBe(4); });
}); });
});

View file

@ -1,5 +1,5 @@
import type { AssetResponseDto } from '@immich/sdk'; import type { AssetResponseDto } from '@immich/sdk';
export const getExifCount = (asset: AssetResponseDto) => { export const getExifCount = (asset: AssetResponseDto) => {
return Object.values(asset.exifInfo ?? {}).filter(Boolean).length; return Object.values(asset.exifInfo ?? {}).filter(Boolean).length;
}; };