mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-04 03:27:09 -05:00 
			
		
		
		
	feat(server): deterministic download order (#7658)
This commit is contained in:
		
							parent
							
								
									8df63b7c94
								
							
						
					
					
						commit
						972d5a3411
					
				@ -90,7 +90,10 @@ describe(DownloadService.name, () => {
 | 
				
			|||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2']));
 | 
					      accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2']));
 | 
				
			||||||
      assetMock.getByIds.mockResolvedValue([assetStub.noResizePath, assetStub.noWebpPath]);
 | 
					      assetMock.getByIds.mockResolvedValue([
 | 
				
			||||||
 | 
					        { ...assetStub.noResizePath, id: 'asset-1' },
 | 
				
			||||||
 | 
					        { ...assetStub.noWebpPath, id: 'asset-2' },
 | 
				
			||||||
 | 
					      ]);
 | 
				
			||||||
      storageMock.createZipStream.mockReturnValue(archiveMock);
 | 
					      storageMock.createZipStream.mockReturnValue(archiveMock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await expect(sut.downloadArchive(authStub.admin, { assetIds: ['asset-1', 'asset-2'] })).resolves.toEqual({
 | 
					      await expect(sut.downloadArchive(authStub.admin, { assetIds: ['asset-1', 'asset-2'] })).resolves.toEqual({
 | 
				
			||||||
@ -110,7 +113,33 @@ describe(DownloadService.name, () => {
 | 
				
			|||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2']));
 | 
					      accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2']));
 | 
				
			||||||
      assetMock.getByIds.mockResolvedValue([assetStub.noResizePath, assetStub.noResizePath]);
 | 
					      assetMock.getByIds.mockResolvedValue([
 | 
				
			||||||
 | 
					        { ...assetStub.noResizePath, id: 'asset-1' },
 | 
				
			||||||
 | 
					        { ...assetStub.noResizePath, id: 'asset-2' },
 | 
				
			||||||
 | 
					      ]);
 | 
				
			||||||
 | 
					      storageMock.createZipStream.mockReturnValue(archiveMock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await expect(sut.downloadArchive(authStub.admin, { assetIds: ['asset-1', 'asset-2'] })).resolves.toEqual({
 | 
				
			||||||
 | 
					        stream: archiveMock.stream,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(archiveMock.addFile).toHaveBeenCalledTimes(2);
 | 
				
			||||||
 | 
					      expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg');
 | 
				
			||||||
 | 
					      expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_123.jpg', 'IMG_123+1.jpg');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should be deterministic', async () => {
 | 
				
			||||||
 | 
					      const archiveMock = {
 | 
				
			||||||
 | 
					        addFile: jest.fn(),
 | 
				
			||||||
 | 
					        finalize: jest.fn(),
 | 
				
			||||||
 | 
					        stream: new Readable(),
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2']));
 | 
				
			||||||
 | 
					      assetMock.getByIds.mockResolvedValue([
 | 
				
			||||||
 | 
					        { ...assetStub.noResizePath, id: 'asset-2' },
 | 
				
			||||||
 | 
					        { ...assetStub.noResizePath, id: 'asset-1' },
 | 
				
			||||||
 | 
					      ]);
 | 
				
			||||||
      storageMock.createZipStream.mockReturnValue(archiveMock);
 | 
					      storageMock.createZipStream.mockReturnValue(archiveMock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await expect(sut.downloadArchive(authStub.admin, { assetIds: ['asset-1', 'asset-2'] })).resolves.toEqual({
 | 
					      await expect(sut.downloadArchive(authStub.admin, { assetIds: ['asset-1', 'asset-2'] })).resolves.toEqual({
 | 
				
			||||||
 | 
				
			|||||||
@ -81,9 +81,16 @@ export class DownloadService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const zip = this.storageRepository.createZipStream();
 | 
					    const zip = this.storageRepository.createZipStream();
 | 
				
			||||||
    const assets = await this.assetRepository.getByIds(dto.assetIds);
 | 
					    const assets = await this.assetRepository.getByIds(dto.assetIds);
 | 
				
			||||||
 | 
					    const assetMap = new Map(assets.map((asset) => [asset.id, asset]));
 | 
				
			||||||
    const paths: Record<string, number> = {};
 | 
					    const paths: Record<string, number> = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (const { originalPath, originalFileName } of assets) {
 | 
					    for (const assetId of dto.assetIds) {
 | 
				
			||||||
 | 
					      const asset = assetMap.get(assetId);
 | 
				
			||||||
 | 
					      if (!asset) {
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const { originalPath, originalFileName } = asset;
 | 
				
			||||||
      const extension = extname(originalPath);
 | 
					      const extension = extname(originalPath);
 | 
				
			||||||
      let filename = `${originalFileName}${extension}`;
 | 
					      let filename = `${originalFileName}${extension}`;
 | 
				
			||||||
      const count = paths[filename] || 0;
 | 
					      const count = paths[filename] || 0;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user