mirror of
https://github.com/immich-app/immich.git
synced 2025-03-31 21:29:38 +02:00
fix(server): queue missing metadata (#15864)
fix: queue missing metadata
This commit is contained in:
parent
7ec3610753
commit
b730aa60ed
3 changed files with 94 additions and 2 deletions
86
e2e/src/api/specs/jobs.e2e-spec.ts
Normal file
86
e2e/src/api/specs/jobs.e2e-spec.ts
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
import { JobCommand, JobName, LoginResponseDto } from '@immich/sdk';
|
||||||
|
import { readFile } from 'node:fs/promises';
|
||||||
|
import { basename } from 'node:path';
|
||||||
|
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,6 +6,8 @@ import {
|
||||||
CheckExistingAssetsDto,
|
CheckExistingAssetsDto,
|
||||||
CreateAlbumDto,
|
CreateAlbumDto,
|
||||||
CreateLibraryDto,
|
CreateLibraryDto,
|
||||||
|
JobCommandDto,
|
||||||
|
JobName,
|
||||||
MetadataSearchDto,
|
MetadataSearchDto,
|
||||||
Permission,
|
Permission,
|
||||||
PersonCreateDto,
|
PersonCreateDto,
|
||||||
|
@ -29,6 +31,7 @@ import {
|
||||||
getConfigDefaults,
|
getConfigDefaults,
|
||||||
login,
|
login,
|
||||||
searchAssets,
|
searchAssets,
|
||||||
|
sendJobCommand,
|
||||||
setBaseUrl,
|
setBaseUrl,
|
||||||
signUpAdmin,
|
signUpAdmin,
|
||||||
tagAssets,
|
tagAssets,
|
||||||
|
@ -475,6 +478,9 @@ export const utils = {
|
||||||
tagAssets: (accessToken: string, tagId: string, assetIds: string[]) =>
|
tagAssets: (accessToken: string, tagId: string, assetIds: string[]) =>
|
||||||
tagAssets({ id: tagId, bulkIdsDto: { ids: assetIds } }, { headers: asBearerAuth(accessToken) }),
|
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') =>
|
setAuthCookies: async (context: BrowserContext, accessToken: string, domain = '127.0.0.1') =>
|
||||||
await context.addCookies([
|
await context.addCookies([
|
||||||
{
|
{
|
||||||
|
|
|
@ -467,8 +467,8 @@ export class AssetRepository implements IAssetRepository {
|
||||||
)
|
)
|
||||||
.$if(property === WithoutProperty.EXIF, (qb) =>
|
.$if(property === WithoutProperty.EXIF, (qb) =>
|
||||||
qb
|
qb
|
||||||
.innerJoin('asset_job_status as job_status', 'assets.id', 'job_status.assetId')
|
.leftJoin('asset_job_status as job_status', 'assets.id', 'job_status.assetId')
|
||||||
.where('job_status.metadataExtractedAt', 'is', null)
|
.where((eb) => eb.or([eb('job_status.metadataExtractedAt', 'is', null), eb('assetId', 'is', null)]))
|
||||||
.where('assets.isVisible', '=', true),
|
.where('assets.isVisible', '=', true),
|
||||||
)
|
)
|
||||||
.$if(property === WithoutProperty.FACES, (qb) =>
|
.$if(property === WithoutProperty.FACES, (qb) =>
|
||||||
|
|
Loading…
Add table
Reference in a new issue