diff --git a/server/package-lock.json b/server/package-lock.json index 3279f5272f14f..93d1282731af5 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -33,6 +33,7 @@ "cookie-parser": "^1.4.6", "exiftool-vendored": "~24.5.0", "exiftool-vendored.pl": "12.76", + "fast-glob": "^3.3.2", "fluent-ffmpeg": "^2.1.2", "geo-tz": "^8.0.0", "glob": "^10.3.3", @@ -2656,7 +2657,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -2669,7 +2669,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "engines": { "node": ">= 8" } @@ -2678,7 +2677,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -6339,7 +6337,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -6372,7 +6369,6 @@ "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, "dependencies": { "reusify": "^1.0.4" } @@ -8659,7 +8655,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "engines": { "node": ">= 8" } @@ -8676,7 +8671,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -8689,7 +8683,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -9951,7 +9944,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -10391,7 +10383,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -10427,7 +10418,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -14419,7 +14409,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "requires": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -14428,14 +14417,12 @@ "@nodelib/fs.stat": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" }, "@nodelib/fs.walk": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -17281,7 +17268,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -17311,7 +17297,6 @@ "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, "requires": { "reusify": "^1.0.4" } @@ -19033,8 +19018,7 @@ "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" }, "methods": { "version": "1.1.2", @@ -19045,7 +19029,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, "requires": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -19054,8 +19037,7 @@ "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" } } }, @@ -19990,8 +19972,7 @@ "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, "queue-tick": { "version": "1.0.1", @@ -20322,8 +20303,7 @@ "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" }, "rimraf": { "version": "5.0.5", @@ -20343,7 +20323,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "requires": { "queue-microtask": "^1.2.2" } diff --git a/server/package.json b/server/package.json index 24f77cb21131a..e0767ea7a04a4 100644 --- a/server/package.json +++ b/server/package.json @@ -58,6 +58,7 @@ "cookie-parser": "^1.4.6", "exiftool-vendored": "~24.5.0", "exiftool-vendored.pl": "12.76", + "fast-glob": "^3.3.2", "fluent-ffmpeg": "^2.1.2", "geo-tz": "^8.0.0", "glob": "^10.3.3", diff --git a/server/src/domain/library/library.service.spec.ts b/server/src/domain/library/library.service.spec.ts index a44624c43abb7..03042cf55a245 100644 --- a/server/src/domain/library/library.service.spec.ts +++ b/server/src/domain/library/library.service.spec.ts @@ -156,8 +156,7 @@ describe(LibraryService.name, () => { libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); storageMock.crawl.mockResolvedValue(['/data/user1/photo.jpg']); - assetMock.getPathsNotInLibrary.mockResolvedValue(['/data/user1/photo.jpg']); - assetMock.getByLibraryId.mockResolvedValue([]); + assetMock.getLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false }); await sut.handleQueueAssetRefresh(mockLibraryJob); @@ -183,7 +182,7 @@ describe(LibraryService.name, () => { libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); storageMock.crawl.mockResolvedValue(['/data/user1/photo.jpg']); - assetMock.getByLibraryId.mockResolvedValue([]); + assetMock.getLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false }); await sut.handleQueueAssetRefresh(mockLibraryJob); @@ -233,7 +232,7 @@ describe(LibraryService.name, () => { libraryMock.get.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1); storageMock.crawl.mockResolvedValue([]); - assetMock.getByLibraryId.mockResolvedValue([]); + assetMock.getLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false }); await sut.handleQueueAssetRefresh(mockLibraryJob); @@ -242,6 +241,48 @@ describe(LibraryService.name, () => { exclusionPatterns: [], }); }); + + it('should set missing assets offline', async () => { + const mockLibraryJob: ILibraryRefreshJob = { + id: libraryStub.externalLibrary1.id, + refreshModifiedFiles: false, + refreshAllFiles: false, + }; + + libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); + storageMock.crawl.mockResolvedValue([]); + assetMock.getLibraryAssetPaths.mockResolvedValue({ + items: [assetStub.image], + hasNextPage: false, + }); + + await sut.handleQueueAssetRefresh(mockLibraryJob); + + expect(assetMock.updateAll).toHaveBeenCalledWith([assetStub.image.id], { isOffline: true }); + expect(assetMock.updateAll).not.toHaveBeenCalledWith(expect.anything(), { isOffline: false }); + expect(jobMock.queueAll).not.toHaveBeenCalled(); + }); + + it('should set crawled assets that were previously offline back online', async () => { + const mockLibraryJob: ILibraryRefreshJob = { + id: libraryStub.externalLibrary1.id, + refreshModifiedFiles: false, + refreshAllFiles: false, + }; + + libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); + storageMock.crawl.mockResolvedValue([assetStub.offline.originalPath]); + assetMock.getLibraryAssetPaths.mockResolvedValue({ + items: [assetStub.offline], + hasNextPage: false, + }); + + await sut.handleQueueAssetRefresh(mockLibraryJob); + + expect(assetMock.updateAll).toHaveBeenCalledWith([assetStub.offline.id], { isOffline: false }); + expect(assetMock.updateAll).not.toHaveBeenCalledWith(expect.anything(), { isOffline: true }); + expect(jobMock.queueAll).not.toHaveBeenCalled(); + }); }); describe('handleAssetRefresh', () => { diff --git a/server/src/domain/library/library.service.ts b/server/src/domain/library/library.service.ts index c74e97ea36db6..25894c9b5a963 100644 --- a/server/src/domain/library/library.service.ts +++ b/server/src/domain/library/library.service.ts @@ -640,27 +640,56 @@ export class LibraryService extends EventEmitter { .filter((validation) => validation.isValid) .map((validation) => validation.importPath); - const rawPaths = await this.storageRepository.crawl({ + let rawPaths = await this.storageRepository.crawl({ pathsToCrawl: validImportPaths, exclusionPatterns: library.exclusionPatterns, }); - const crawledAssetPaths = rawPaths.map((filePath) => path.normalize(filePath)); + const crawledAssetPaths = new Set(rawPaths); - this.logger.debug(`Found ${crawledAssetPaths.length} asset(s) when crawling import paths ${library.importPaths}`); + const shouldScanAll = job.refreshAllFiles || job.refreshModifiedFiles; + let pathsToScan: string[] = shouldScanAll ? rawPaths : []; + rawPaths = []; - await this.assetRepository.updateOfflineLibraryAssets(library.id, crawledAssetPaths); + this.logger.debug(`Found ${crawledAssetPaths.size} asset(s) when crawling import paths ${library.importPaths}`); - if (crawledAssetPaths.length > 0) { - let filteredPaths: string[] = []; - if (job.refreshAllFiles || job.refreshModifiedFiles) { - filteredPaths = crawledAssetPaths; - } else { - filteredPaths = await this.assetRepository.getPathsNotInLibrary(library.id, crawledAssetPaths); + const assetIdsToMarkOffline = []; + const assetIdsToMarkOnline = []; + const pagination = usePagination(5000, (pagination) => + this.assetRepository.getLibraryAssetPaths(pagination, library.id), + ); + + for await (const page of pagination) { + for (const asset of page) { + const isOffline = !crawledAssetPaths.has(asset.originalPath); + if (isOffline && !asset.isOffline) { + assetIdsToMarkOffline.push(asset.id); + } - this.logger.debug(`Will import ${filteredPaths.length} new asset(s)`); + if (!isOffline && asset.isOffline) { + assetIdsToMarkOnline.push(asset.id); + } + + crawledAssetPaths.delete(asset.originalPath); } + } + + if (assetIdsToMarkOffline.length > 0) { + this.logger.debug(`Found ${assetIdsToMarkOffline.length} offline asset(s) previously marked as online`); + await this.assetRepository.updateAll(assetIdsToMarkOffline, { isOffline: true }); + } + + if (assetIdsToMarkOnline.length > 0) { + this.logger.debug(`Found ${assetIdsToMarkOnline.length} online asset(s) previously marked as offline`); + await this.assetRepository.updateAll(assetIdsToMarkOnline, { isOffline: false }); + } + + if (!shouldScanAll) { + pathsToScan = [...crawledAssetPaths]; + this.logger.debug(`Will import ${pathsToScan.length} new asset(s)`); + } - await this.scanAssets(job.id, filteredPaths, library.ownerId, job.refreshAllFiles ?? false); + if (pathsToScan.length > 0) { + await this.scanAssets(job.id, pathsToScan, library.ownerId, job.refreshAllFiles ?? false); } await this.repository.update({ id: job.id, refreshedAt: new Date() }); diff --git a/server/src/domain/repositories/asset.repository.ts b/server/src/domain/repositories/asset.repository.ts index 847c97aae3971..b779c8b8c3a22 100644 --- a/server/src/domain/repositories/asset.repository.ts +++ b/server/src/domain/repositories/asset.repository.ts @@ -109,6 +109,8 @@ export interface MetadataSearchOptions { numResults: number; } +export type AssetPathEntity = Pick; + export const IAssetRepository = 'IAssetRepository'; export interface IAssetRepository { @@ -129,10 +131,8 @@ export interface IAssetRepository { getRandom(userId: string, count: number): Promise; getFirstAssetForAlbumId(albumId: string): Promise; getLastUpdatedAssetForAlbumId(albumId: string): Promise; - getByLibraryId(libraryIds: string[]): Promise; + getLibraryAssetPaths(pagination: PaginationOptions, libraryId: string): Paginated; getByLibraryIdAndOriginalPath(libraryId: string, originalPath: string): Promise; - getPathsNotInLibrary(libraryId: string, originalPaths: string[]): Promise; - updateOfflineLibraryAssets(libraryId: string, originalPaths: string[]): Promise; deleteAll(ownerId: string): Promise; getAll(pagination: PaginationOptions, options?: AssetSearchOptions): Paginated; getAllByDeviceId(userId: string, deviceId: string): Promise; diff --git a/server/src/infra/repositories/asset.repository.ts b/server/src/infra/repositories/asset.repository.ts index 15aa11523a4ac..ff60be9fe0f00 100644 --- a/server/src/infra/repositories/asset.repository.ts +++ b/server/src/infra/repositories/asset.repository.ts @@ -2,6 +2,7 @@ import { AssetBuilderOptions, AssetCreate, AssetExploreFieldOptions, + AssetPathEntity, AssetSearchOptions, AssetStats, AssetStatsOptions, @@ -184,10 +185,10 @@ export class AssetRepository implements IAssetRepository { } @GenerateSql({ params: [[DummyValue.UUID]] }) - @ChunkedArray() - getByLibraryId(libraryIds: string[]): Promise { - return this.repository.find({ - where: { library: { id: In(libraryIds) } }, + getLibraryAssetPaths(pagination: PaginationOptions, libraryId: string): Paginated { + return paginate(this.repository, pagination, { + select: { id: true, originalPath: true, isOffline: true }, + where: { library: { id: libraryId } }, }); } diff --git a/server/src/infra/repositories/filesystem.provider.ts b/server/src/infra/repositories/filesystem.provider.ts index fef184992d830..32880ae181984 100644 --- a/server/src/infra/repositories/filesystem.provider.ts +++ b/server/src/infra/repositories/filesystem.provider.ts @@ -11,7 +11,7 @@ import { import { ImmichLogger } from '@app/infra/logger'; import archiver from 'archiver'; import chokidar, { WatchOptions } from 'chokidar'; -import { glob } from 'glob'; +import { glob } from 'fast-glob'; import { constants, createReadStream, existsSync, mkdirSync } from 'node:fs'; import fs, { copyFile, readdir, rename, stat, utimes, writeFile } from 'node:fs/promises'; import path from 'node:path'; @@ -123,7 +123,7 @@ export class FilesystemProvider implements IStorageRepository { crawl(crawlOptions: CrawlOptionsDto): Promise { const { pathsToCrawl, exclusionPatterns, includeHidden } = crawlOptions; - if (!pathsToCrawl) { + if (pathsToCrawl.length === 0) { return Promise.resolve([]); } @@ -132,8 +132,8 @@ export class FilesystemProvider implements IStorageRepository { return glob(`${base}/**/${extensions}`, { absolute: true, - nocase: true, - nodir: true, + caseSensitiveMatch: false, + onlyFiles: true, dot: includeHidden, ignore: exclusionPatterns, }); diff --git a/server/src/infra/sql/asset.repository.sql b/server/src/infra/sql/asset.repository.sql index 4e5dd2536bcfc..75b5291b66914 100644 --- a/server/src/infra/sql/asset.repository.sql +++ b/server/src/infra/sql/asset.repository.sql @@ -293,53 +293,6 @@ DELETE FROM "assets" WHERE "ownerId" = $1 --- AssetRepository.getByLibraryId -SELECT - "AssetEntity"."id" AS "AssetEntity_id", - "AssetEntity"."deviceAssetId" AS "AssetEntity_deviceAssetId", - "AssetEntity"."ownerId" AS "AssetEntity_ownerId", - "AssetEntity"."libraryId" AS "AssetEntity_libraryId", - "AssetEntity"."deviceId" AS "AssetEntity_deviceId", - "AssetEntity"."type" AS "AssetEntity_type", - "AssetEntity"."originalPath" AS "AssetEntity_originalPath", - "AssetEntity"."resizePath" AS "AssetEntity_resizePath", - "AssetEntity"."webpPath" AS "AssetEntity_webpPath", - "AssetEntity"."thumbhash" AS "AssetEntity_thumbhash", - "AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath", - "AssetEntity"."createdAt" AS "AssetEntity_createdAt", - "AssetEntity"."updatedAt" AS "AssetEntity_updatedAt", - "AssetEntity"."deletedAt" AS "AssetEntity_deletedAt", - "AssetEntity"."fileCreatedAt" AS "AssetEntity_fileCreatedAt", - "AssetEntity"."localDateTime" AS "AssetEntity_localDateTime", - "AssetEntity"."fileModifiedAt" AS "AssetEntity_fileModifiedAt", - "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", - "AssetEntity"."isArchived" AS "AssetEntity_isArchived", - "AssetEntity"."isExternal" AS "AssetEntity_isExternal", - "AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly", - "AssetEntity"."isOffline" AS "AssetEntity_isOffline", - "AssetEntity"."checksum" AS "AssetEntity_checksum", - "AssetEntity"."duration" AS "AssetEntity_duration", - "AssetEntity"."isVisible" AS "AssetEntity_isVisible", - "AssetEntity"."livePhotoVideoId" AS "AssetEntity_livePhotoVideoId", - "AssetEntity"."originalFileName" AS "AssetEntity_originalFileName", - "AssetEntity"."sidecarPath" AS "AssetEntity_sidecarPath", - "AssetEntity"."stackId" AS "AssetEntity_stackId" -FROM - "assets" "AssetEntity" - LEFT JOIN "libraries" "AssetEntity__AssetEntity_library" ON "AssetEntity__AssetEntity_library"."id" = "AssetEntity"."libraryId" - AND ( - "AssetEntity__AssetEntity_library"."deletedAt" IS NULL - ) -WHERE - ( - ( - ( - (("AssetEntity__AssetEntity_library"."id" IN ($1))) - ) - ) - ) - AND ("AssetEntity"."deletedAt" IS NULL) - -- AssetRepository.getByLibraryIdAndOriginalPath SELECT DISTINCT "distinctAlias"."AssetEntity_id" AS "ids_AssetEntity_id" diff --git a/server/test/repositories/asset.repository.mock.ts b/server/test/repositories/asset.repository.mock.ts index 6143d357c148f..e1a5fed83097f 100644 --- a/server/test/repositories/asset.repository.mock.ts +++ b/server/test/repositories/asset.repository.mock.ts @@ -20,10 +20,8 @@ export const newAssetRepositoryMock = (): jest.Mocked => { getAll: jest.fn().mockResolvedValue({ items: [], hasNextPage: false }), getAllByDeviceId: jest.fn(), updateAll: jest.fn(), - getByLibraryId: jest.fn(), + getLibraryAssetPaths: jest.fn(), getByLibraryIdAndOriginalPath: jest.fn(), - updateOfflineLibraryAssets: jest.fn(), - getPathsNotInLibrary: jest.fn(), deleteAll: jest.fn(), save: jest.fn(), remove: jest.fn(),