diff --git a/server/e2e/jobs/specs/library.e2e-spec.ts b/server/e2e/jobs/specs/library.e2e-spec.ts index 70dc578e8..b068ae9e8 100644 --- a/server/e2e/jobs/specs/library.e2e-spec.ts +++ b/server/e2e/jobs/specs/library.e2e-spec.ts @@ -35,48 +35,6 @@ describe(`${LibraryController.name} (e2e)`, () => { }); describe('POST /library/:id/scan', () => { - it('should mark a rediscovered file as back online', async () => { - await fs.promises.cp(`${IMMICH_TEST_ASSET_PATH}/albums/nature`, `${IMMICH_TEST_ASSET_TEMP_PATH}/albums/nature`, { - recursive: true, - }); - - const library = await api.libraryApi.create(server, admin.accessToken, { - ownerId: admin.userId, - type: LibraryType.EXTERNAL, - importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`], - }); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - const onlineAssets = await api.assetApi.getAllAssets(server, admin.accessToken); - expect(onlineAssets.length).toBeGreaterThan(1); - - await fs.promises.rm(`${IMMICH_TEST_ASSET_TEMP_PATH}/albums/nature/silver_fir.jpg`); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - await fs.promises.cp(`${IMMICH_TEST_ASSET_PATH}/albums/nature`, `${IMMICH_TEST_ASSET_TEMP_PATH}/albums/nature`, { - recursive: true, - }); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - const assets = await api.assetApi.getAllAssets(server, admin.accessToken); - - expect(assets).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - isOffline: false, - originalFileName: 'silver_fir.jpg', - }), - expect.objectContaining({ - isOffline: false, - originalFileName: 'tanners_ridge.jpg', - }), - ]), - ); - }); - it('should scan new files', async () => { const library = await api.libraryApi.create(server, admin.accessToken, { ownerId: admin.userId, @@ -246,9 +204,7 @@ describe(`${LibraryController.name} (e2e)`, () => { ); }); }); - }); - describe('POST /library/:id/scanDeleted', () => { it('should offline a missing file', async () => { await fs.promises.cp(`${IMMICH_TEST_ASSET_PATH}/albums/nature`, `${IMMICH_TEST_ASSET_TEMP_PATH}/albums/nature`, { recursive: true, @@ -284,6 +240,149 @@ describe(`${LibraryController.name} (e2e)`, () => { ]), ); }); + + it('should offline a file on disk not in import paths', async () => { + await fs.promises.cp(`${IMMICH_TEST_ASSET_PATH}/albums/nature`, `${IMMICH_TEST_ASSET_TEMP_PATH}/albums/nature`, { + recursive: true, + }); + + await fs.promises.mkdir(`${IMMICH_TEST_ASSET_TEMP_PATH}/albums/other`); + + const library = await api.libraryApi.create(server, admin.accessToken, { + type: LibraryType.EXTERNAL, + ownerId: admin.userId, + importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}/albums`], + }); + + await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); + + const onlineAssets = await api.assetApi.getAllAssets(server, admin.accessToken); + expect(onlineAssets.length).toBeGreaterThan(1); + + await api.libraryApi.setImportPaths(server, admin.accessToken, library.id, [ + `${IMMICH_TEST_ASSET_TEMP_PATH}/albums/other`, + ]); + + await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); + + const assets = await api.assetApi.getAllAssets(server, admin.accessToken); + + expect(assets).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + isOffline: true, + originalFileName: 'silver_fir.jpg', + }), + expect.objectContaining({ + isOffline: true, + originalFileName: 'tanners_ridge.jpg', + }), + ]), + ); + }); + + it('should mark a rediscovered file as back online', async () => { + await fs.promises.cp(`${IMMICH_TEST_ASSET_PATH}/albums/nature`, `${IMMICH_TEST_ASSET_TEMP_PATH}/albums/nature`, { + recursive: true, + }); + + const library = await api.libraryApi.create(server, admin.accessToken, { + ownerId: admin.userId, + type: LibraryType.EXTERNAL, + importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`], + }); + + await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); + + const onlineAssets = await api.assetApi.getAllAssets(server, admin.accessToken); + expect(onlineAssets.length).toBeGreaterThan(1); + + await fs.promises.rm(`${IMMICH_TEST_ASSET_TEMP_PATH}/albums/nature/silver_fir.jpg`); + + await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); + + await fs.promises.cp(`${IMMICH_TEST_ASSET_PATH}/albums/nature`, `${IMMICH_TEST_ASSET_TEMP_PATH}/albums/nature`, { + recursive: true, + }); + + await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); + + const assets = await api.assetApi.getAllAssets(server, admin.accessToken); + + expect(assets).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + isOffline: false, + originalFileName: 'silver_fir.jpg', + }), + expect.objectContaining({ + isOffline: false, + originalFileName: 'tanners_ridge.jpg', + }), + ]), + ); + }); + + it('should mark a rediscovered file in import paths as back online', async () => { + await fs.promises.cp(`${IMMICH_TEST_ASSET_PATH}/albums/nature`, `${IMMICH_TEST_ASSET_TEMP_PATH}/albums/nature`, { + recursive: true, + }); + + await fs.promises.mkdir(`${IMMICH_TEST_ASSET_TEMP_PATH}/albums/other`); + + const library = await api.libraryApi.create(server, admin.accessToken, { + ownerId: admin.userId, + type: LibraryType.EXTERNAL, + importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}/albums/`], + }); + + await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); + + const initialAssets = await api.assetApi.getAllAssets(server, admin.accessToken); + expect(initialAssets.length).toBeGreaterThan(1); + + await api.libraryApi.setImportPaths(server, admin.accessToken, library.id, [ + `${IMMICH_TEST_ASSET_TEMP_PATH}/albums/other`, + ]); + + await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); + + const offlineAssets = await api.assetApi.getAllAssets(server, admin.accessToken); + + expect(offlineAssets).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + isOffline: true, + originalFileName: 'silver_fir.jpg', + }), + expect.objectContaining({ + isOffline: true, + originalFileName: 'tanners_ridge.jpg', + }), + ]), + ); + + await api.libraryApi.setImportPaths(server, admin.accessToken, library.id, [ + `${IMMICH_TEST_ASSET_TEMP_PATH}/albums/nature`, + ]); + + await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); + + const onlineAssets = await api.assetApi.getAllAssets(server, admin.accessToken); + + expect(onlineAssets).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + isOffline: false, + originalFileName: 'silver_fir.jpg', + }), + expect.objectContaining({ + isOffline: false, + originalFileName: 'tanners_ridge.jpg', + }), + ]), + ); + }); }); describe('POST /library/:id/removeOffline', () => { diff --git a/server/src/domain/library/library.service.ts b/server/src/domain/library/library.service.ts index ca0357448..e7ae2a709 100644 --- a/server/src/domain/library/library.service.ts +++ b/server/src/domain/library/library.service.ts @@ -479,6 +479,7 @@ export class LibraryService extends EventEmitter { sidecarPath = `${assetPath}.xmp`; } + // TODO: device asset id is deprecated, remove it const deviceAssetId = `${basename(assetPath)}`.replaceAll(/\s+/g, ''); let assetId; @@ -573,7 +574,7 @@ export class LibraryService extends EventEmitter { return JobStatus.SUCCESS; } - // Check if an asset is has no file, marking it as offline + // Check if an asset is has no file or is outside of import paths, marking it as offline async handleOfflineCheck(job: IEntityJob): Promise { const asset = await this.assetRepository.getById(job.id);