Skip to content

Commit

Permalink
fix: queue missing metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
etnoy committed Feb 3, 2025
1 parent 96a6cc2 commit a2d90a6
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 2 deletions.
87 changes: 87 additions & 0 deletions e2e/src/api/specs/jobs.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { JobCommand, JobName, LoginResponseDto } from '@immich/sdk';
import { readFile } from 'fs/promises';

Check failure on line 2 in e2e/src/api/specs/jobs.e2e-spec.ts

View workflow job for this annotation

GitHub Actions / End-to-End Lint

Prefer `node:fs/promises` over `fs/promises`
import { basename } from 'path';

Check failure on line 3 in e2e/src/api/specs/jobs.e2e-spec.ts

View workflow job for this annotation

GitHub Actions / End-to-End Lint

Prefer `node:path` over `path`
import { createUserDto } from 'src/fixtures';

Check failure on line 4 in e2e/src/api/specs/jobs.e2e-spec.ts

View workflow job for this annotation

GitHub Actions / End-to-End Lint

'createUserDto' is defined but never used
import { errorDto } from 'src/responses';
import { app, testAssetDir, utils } from 'src/utils';
import request from 'supertest';
import { afterEach, beforeAll, describe, expect, it } from 'vitest';

describe('/jobs', () => {
let admin: LoginResponseDto;

beforeAll(async () => {
await utils.resetDatabase();
admin = await utils.adminSetup({ onboarding: false });
});

describe('PUT /jobs', () => {
afterEach(async () => {
await utils.jobCommand(admin.accessToken, JobName.MetadataExtraction, {
command: JobCommand.Resume,
force: false,
});
});

it('should require authentication', async () => {
const { status, body } = await request(app).put('/jobs/metadataExtraction');
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});

it('should queue metadata extraction for missing assets', async () => {
const path1 = `${testAssetDir}/formats/raw/Nikon/D700/philadelphia.nef`;
const path2 = `${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`;

await utils.createAsset(admin.accessToken, {
assetData: { bytes: await readFile(path1), filename: basename(path1) },
});

await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');

await utils.jobCommand(admin.accessToken, JobName.MetadataExtraction, {
command: JobCommand.Pause,
force: false,
});

const { id } = await utils.createAsset(admin.accessToken, {
assetData: { bytes: await readFile(path2), filename: basename(path2) },
});

await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');

{
const asset = await utils.getAssetInfo(admin.accessToken, id);

expect(asset.exifInfo).toBeDefined();
expect(asset.exifInfo?.make).toBeNull();
}

await utils.jobCommand(admin.accessToken, JobName.MetadataExtraction, {
command: JobCommand.Empty,
force: false,
});

await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');

await utils.jobCommand(admin.accessToken, JobName.MetadataExtraction, {
command: JobCommand.Resume,
force: false,
});

await utils.jobCommand(admin.accessToken, JobName.MetadataExtraction, {
command: JobCommand.Start,
force: false,
});

await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');

{
const asset = await utils.getAssetInfo(admin.accessToken, id);

expect(asset.exifInfo).toBeDefined();
expect(asset.exifInfo?.make).toBe('NIKON CORPORATION');
}
});
});
});
6 changes: 6 additions & 0 deletions e2e/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
CheckExistingAssetsDto,
CreateAlbumDto,
CreateLibraryDto,
JobCommandDto,
JobName,
MetadataSearchDto,
Permission,
PersonCreateDto,
Expand All @@ -29,6 +31,7 @@ import {
getConfigDefaults,
login,
searchAssets,
sendJobCommand,
setBaseUrl,
signUpAdmin,
tagAssets,
Expand Down Expand Up @@ -475,6 +478,9 @@ export const utils = {
tagAssets: (accessToken: string, tagId: string, assetIds: string[]) =>
tagAssets({ id: tagId, bulkIdsDto: { ids: assetIds } }, { headers: asBearerAuth(accessToken) }),

jobCommand: async (accessToken: string, jobName: JobName, jobCommandDto: JobCommandDto) =>
sendJobCommand({ id: jobName, jobCommandDto }, { headers: asBearerAuth(accessToken) }),

setAuthCookies: async (context: BrowserContext, accessToken: string, domain = '127.0.0.1') =>
await context.addCookies([
{
Expand Down
4 changes: 2 additions & 2 deletions server/src/repositories/asset.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,8 +466,8 @@ export class AssetRepository implements IAssetRepository {
)
.$if(property === WithoutProperty.EXIF, (qb) =>
qb
.innerJoin('asset_job_status as job_status', 'assets.id', 'job_status.assetId')
.where('job_status.metadataExtractedAt', 'is', null)
.leftJoin('asset_job_status as job_status', 'assets.id', 'job_status.assetId')
.where((eb) => eb.or([eb('job_status.metadataExtractedAt', 'is', null), eb('assetId', 'is', null)]))
.where('assets.isVisible', '=', true),
)
.$if(property === WithoutProperty.FACES, (qb) =>
Expand Down

0 comments on commit a2d90a6

Please sign in to comment.