mirror of
https://github.com/immich-app/immich.git
synced 2025-12-21 01:11:16 +03:00
fix(server): external library motion photo video asset handling (#8721)
* added "isExternal" to the getLibraryAssetPaths query * handleQueueAssetRefresh skip "non external" video asset, closes #8562 * correctly implements live photo deletion for external library * use "external asset" for external library tests * minor: external library asset checksum is "path hash" not file hash * renamed to getExternalLibraryAssetPaths and added isExternal where clause * generated sql * reverted leftover change
This commit is contained in:
@@ -155,7 +155,7 @@ export interface IAssetRepository {
|
||||
getRandom(userId: string, count: number): Promise<AssetEntity[]>;
|
||||
getFirstAssetForAlbumId(albumId: string): Promise<AssetEntity | null>;
|
||||
getLastUpdatedAssetForAlbumId(albumId: string): Promise<AssetEntity | null>;
|
||||
getLibraryAssetPaths(pagination: PaginationOptions, libraryId: string): Paginated<AssetPathEntity>;
|
||||
getExternalLibraryAssetPaths(pagination: PaginationOptions, libraryId: string): Paginated<AssetPathEntity>;
|
||||
getByLibraryIdAndOriginalPath(libraryId: string, originalPath: string): Promise<AssetEntity | null>;
|
||||
deleteAll(ownerId: string): Promise<void>;
|
||||
getAll(pagination: PaginationOptions, options?: AssetSearchOptions): Paginated<AssetEntity>;
|
||||
|
||||
@@ -253,7 +253,7 @@ DELETE FROM "assets"
|
||||
WHERE
|
||||
"ownerId" = $1
|
||||
|
||||
-- AssetRepository.getLibraryAssetPaths
|
||||
-- AssetRepository.getExternalLibraryAssetPaths
|
||||
SELECT DISTINCT
|
||||
"distinctAlias"."AssetEntity_id" AS "ids_AssetEntity_id"
|
||||
FROM
|
||||
@@ -272,6 +272,7 @@ FROM
|
||||
(
|
||||
(
|
||||
((("AssetEntity__AssetEntity_library"."id" = $1)))
|
||||
AND ("AssetEntity"."isExternal" = $2)
|
||||
)
|
||||
)
|
||||
AND ("AssetEntity"."deletedAt" IS NULL)
|
||||
|
||||
@@ -160,10 +160,10 @@ export class AssetRepository implements IAssetRepository {
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [{ take: 1, skip: 0 }, DummyValue.UUID] })
|
||||
getLibraryAssetPaths(pagination: PaginationOptions, libraryId: string): Paginated<AssetPathEntity> {
|
||||
getExternalLibraryAssetPaths(pagination: PaginationOptions, libraryId: string): Paginated<AssetPathEntity> {
|
||||
return paginate(this.repository, pagination, {
|
||||
select: { id: true, originalPath: true, isOffline: true },
|
||||
where: { library: { id: libraryId } },
|
||||
where: { library: { id: libraryId }, isExternal: true },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -396,7 +396,10 @@ export class AssetService {
|
||||
|
||||
// TODO refactor this to use cascades
|
||||
if (asset.livePhotoVideoId) {
|
||||
await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id: asset.livePhotoVideoId } });
|
||||
await this.jobRepository.queue({
|
||||
name: JobName.ASSET_DELETION,
|
||||
data: { id: asset.livePhotoVideoId, fromExternal },
|
||||
});
|
||||
}
|
||||
|
||||
const files = [asset.thumbnailPath, asset.previewPath, asset.encodedVideoPath];
|
||||
|
||||
@@ -160,7 +160,7 @@ describe(LibraryService.name, () => {
|
||||
storageMock.walk.mockImplementation(async function* generator() {
|
||||
yield '/data/user1/photo.jpg';
|
||||
});
|
||||
assetMock.getLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false });
|
||||
assetMock.getExternalLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false });
|
||||
|
||||
await sut.handleQueueAssetRefresh(mockLibraryJob);
|
||||
|
||||
@@ -189,7 +189,7 @@ describe(LibraryService.name, () => {
|
||||
storageMock.walk.mockImplementation(async function* generator() {
|
||||
yield '/data/user1/photo.jpg';
|
||||
});
|
||||
assetMock.getLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false });
|
||||
assetMock.getExternalLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false });
|
||||
|
||||
await sut.handleQueueAssetRefresh(mockLibraryJob);
|
||||
|
||||
@@ -238,7 +238,7 @@ describe(LibraryService.name, () => {
|
||||
};
|
||||
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1);
|
||||
assetMock.getLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false });
|
||||
assetMock.getExternalLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false });
|
||||
|
||||
await sut.handleQueueAssetRefresh(mockLibraryJob);
|
||||
|
||||
@@ -256,8 +256,8 @@ describe(LibraryService.name, () => {
|
||||
};
|
||||
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1);
|
||||
assetMock.getLibraryAssetPaths.mockResolvedValue({
|
||||
items: [assetStub.image],
|
||||
assetMock.getExternalLibraryAssetPaths.mockResolvedValue({
|
||||
items: [assetStub.external],
|
||||
hasNextPage: false,
|
||||
});
|
||||
|
||||
@@ -278,16 +278,16 @@ describe(LibraryService.name, () => {
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1);
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
storageMock.walk.mockImplementation(async function* generator() {
|
||||
yield assetStub.offline.originalPath;
|
||||
yield assetStub.externalOffline.originalPath;
|
||||
});
|
||||
assetMock.getLibraryAssetPaths.mockResolvedValue({
|
||||
items: [assetStub.offline],
|
||||
assetMock.getExternalLibraryAssetPaths.mockResolvedValue({
|
||||
items: [assetStub.externalOffline],
|
||||
hasNextPage: false,
|
||||
});
|
||||
|
||||
await sut.handleQueueAssetRefresh(mockLibraryJob);
|
||||
|
||||
expect(assetMock.updateAll).toHaveBeenCalledWith([assetStub.offline.id], { isOffline: false });
|
||||
expect(assetMock.updateAll).toHaveBeenCalledWith([assetStub.externalOffline.id], { isOffline: false });
|
||||
expect(assetMock.updateAll).not.toHaveBeenCalledWith(expect.anything(), { isOffline: true });
|
||||
expect(jobMock.queueAll).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -616,7 +616,7 @@ export class LibraryService extends EventEmitter {
|
||||
const assetIdsToMarkOffline = [];
|
||||
const assetIdsToMarkOnline = [];
|
||||
const pagination = usePagination(LIBRARY_SCAN_BATCH_SIZE, (pagination) =>
|
||||
this.assetRepository.getLibraryAssetPaths(pagination, library.id),
|
||||
this.assetRepository.getExternalLibraryAssetPaths(pagination, library.id),
|
||||
);
|
||||
|
||||
this.logger.verbose(`Crawled asset paths paginated`);
|
||||
|
||||
Reference in New Issue
Block a user