forked from Cutlery/immich
check import paths when testing offline
This commit is contained in:
parent
9a707874e4
commit
f5073a1a7a
@ -21,6 +21,10 @@ export interface ILibraryRefreshJob extends IEntityJob {
|
|||||||
refreshAllFiles: boolean;
|
refreshAllFiles: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ILibraryOfflineJob extends IEntityJob {
|
||||||
|
importPaths: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface IBulkEntityJob extends IBaseJob {
|
export interface IBulkEntityJob extends IBaseJob {
|
||||||
ids: string[];
|
ids: string[];
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ import {
|
|||||||
import { when } from 'jest-when';
|
import { when } from 'jest-when';
|
||||||
import { R_OK } from 'node:constants';
|
import { R_OK } from 'node:constants';
|
||||||
import { Stats } from 'node:fs';
|
import { Stats } from 'node:fs';
|
||||||
import { IEntityJob, ILibraryFileJob, ILibraryRefreshJob, JobName } from '../job';
|
import { IEntityJob, ILibraryFileJob, ILibraryOfflineJob, ILibraryRefreshJob, JobName } from '../job';
|
||||||
import {
|
import {
|
||||||
IAssetRepository,
|
IAssetRepository,
|
||||||
ICryptoRepository,
|
ICryptoRepository,
|
||||||
@ -277,8 +277,9 @@ describe(LibraryService.name, () => {
|
|||||||
|
|
||||||
describe('handleOfflineCheck', () => {
|
describe('handleOfflineCheck', () => {
|
||||||
it('should set missing assets offline', async () => {
|
it('should set missing assets offline', async () => {
|
||||||
const mockAssetJob: IEntityJob = {
|
const mockAssetJob: ILibraryOfflineJob = {
|
||||||
id: assetStub.external.id,
|
id: assetStub.external.id,
|
||||||
|
importPaths: ['/'],
|
||||||
};
|
};
|
||||||
|
|
||||||
assetMock.getById.mockResolvedValue(assetStub.external);
|
assetMock.getById.mockResolvedValue(assetStub.external);
|
||||||
@ -290,9 +291,25 @@ describe(LibraryService.name, () => {
|
|||||||
expect(assetMock.update).toHaveBeenCalledWith({ id: assetStub.external.id, isOffline: true });
|
expect(assetMock.update).toHaveBeenCalledWith({ id: assetStub.external.id, isOffline: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should skip an offline asset', async () => {
|
it('should set an asset outside of import paths as offline', async () => {
|
||||||
const mockAssetJob: IEntityJob = {
|
const mockAssetJob: ILibraryOfflineJob = {
|
||||||
id: assetStub.external.id,
|
id: assetStub.external.id,
|
||||||
|
importPaths: ['/data/user2'],
|
||||||
|
};
|
||||||
|
|
||||||
|
assetMock.getById.mockResolvedValue(assetStub.external);
|
||||||
|
|
||||||
|
storageMock.checkFileExists.mockResolvedValue(true);
|
||||||
|
|
||||||
|
await sut.handleOfflineCheck(mockAssetJob);
|
||||||
|
|
||||||
|
expect(assetMock.update).toHaveBeenCalledWith({ id: assetStub.external.id, isOffline: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip an offline asset', async () => {
|
||||||
|
const mockAssetJob: ILibraryOfflineJob = {
|
||||||
|
id: assetStub.external.id,
|
||||||
|
importPaths: ['/'],
|
||||||
};
|
};
|
||||||
|
|
||||||
assetMock.getById.mockResolvedValue(assetStub.offline);
|
assetMock.getById.mockResolvedValue(assetStub.offline);
|
||||||
@ -306,8 +323,9 @@ describe(LibraryService.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should skip a nonexistent asset id', async () => {
|
it('should skip a nonexistent asset id', async () => {
|
||||||
const mockAssetJob: IEntityJob = {
|
const mockAssetJob: ILibraryOfflineJob = {
|
||||||
id: assetStub.external.id,
|
id: assetStub.external.id,
|
||||||
|
importPaths: ['/'],
|
||||||
};
|
};
|
||||||
|
|
||||||
assetMock.getById.mockImplementation(() => Promise.resolve(null));
|
assetMock.getById.mockImplementation(() => Promise.resolve(null));
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { AssetType, LibraryType } from '@app/infra/entities';
|
import { AssetEntity, AssetType, LibraryType } from '@app/infra/entities';
|
||||||
import { ImmichLogger } from '@app/infra/logger';
|
import { ImmichLogger } from '@app/infra/logger';
|
||||||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||||
import { OnEvent } from '@nestjs/event-emitter';
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
@ -10,7 +10,15 @@ import picomatch from 'picomatch';
|
|||||||
import { AccessCore } from '../access';
|
import { AccessCore } from '../access';
|
||||||
import { mimeTypes } from '../domain.constant';
|
import { mimeTypes } from '../domain.constant';
|
||||||
import { handlePromiseError, usePagination, validateCronExpression } from '../domain.util';
|
import { handlePromiseError, usePagination, validateCronExpression } from '../domain.util';
|
||||||
import { IBaseJob, IEntityJob, ILibraryFileJob, ILibraryRefreshJob, JOBS_ASSET_PAGINATION_SIZE, JobName } from '../job';
|
import {
|
||||||
|
IBaseJob,
|
||||||
|
IEntityJob,
|
||||||
|
ILibraryFileJob,
|
||||||
|
ILibraryOfflineJob,
|
||||||
|
ILibraryRefreshJob,
|
||||||
|
JOBS_ASSET_PAGINATION_SIZE,
|
||||||
|
JobName,
|
||||||
|
} from '../job';
|
||||||
import {
|
import {
|
||||||
DatabaseLock,
|
DatabaseLock,
|
||||||
IAccessRepository,
|
IAccessRepository,
|
||||||
@ -575,7 +583,7 @@ export class LibraryService extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if an asset is has no file or is outside of import paths, 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<JobStatus> {
|
async handleOfflineCheck(job: ILibraryOfflineJob): Promise<JobStatus> {
|
||||||
const asset = await this.assetRepository.getById(job.id);
|
const asset = await this.assetRepository.getById(job.id);
|
||||||
|
|
||||||
if (!asset || asset.isOffline) {
|
if (!asset || asset.isOffline) {
|
||||||
@ -585,7 +593,16 @@ export class LibraryService extends EventEmitter {
|
|||||||
|
|
||||||
const exists = await this.storageRepository.checkFileExists(asset.originalPath, R_OK);
|
const exists = await this.storageRepository.checkFileExists(asset.originalPath, R_OK);
|
||||||
|
|
||||||
if (exists) {
|
let existsInImportPath = false;
|
||||||
|
|
||||||
|
for (const importPath of job.importPaths) {
|
||||||
|
if (asset.originalPath.startsWith(importPath)) {
|
||||||
|
existsInImportPath = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exists && existsInImportPath) {
|
||||||
this.logger.verbose(`Asset is still online: ${asset.originalPath}`);
|
this.logger.verbose(`Asset is still online: ${asset.originalPath}`);
|
||||||
} else {
|
} else {
|
||||||
this.logger.debug(`Marking asset as offline: ${asset.originalPath}`);
|
this.logger.debug(`Marking asset as offline: ${asset.originalPath}`);
|
||||||
@ -657,9 +674,9 @@ export class LibraryService extends EventEmitter {
|
|||||||
`Queuing online check of ${existingAssetPage.value.length} asset(s) in library ${library.id}...`,
|
`Queuing online check of ${existingAssetPage.value.length} asset(s) in library ${library.id}...`,
|
||||||
);
|
);
|
||||||
await this.jobRepository.queueAll(
|
await this.jobRepository.queueAll(
|
||||||
existingAssetPage.value.map((asset) => ({
|
existingAssetPage.value.map((asset: AssetEntity) => ({
|
||||||
name: JobName.LIBRARY_CHECK_OFFLINE,
|
name: JobName.LIBRARY_CHECK_OFFLINE,
|
||||||
data: { id: asset.id },
|
data: { id: asset.id, importPaths: validImportPaths },
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user