mirror of
https://github.com/immich-app/immich.git
synced 2025-01-10 13:56:47 +01:00
78 lines
2.7 KiB
TypeScript
78 lines
2.7 KiB
TypeScript
|
import { AssetType } from '@app/infra/entities';
|
||
|
import { Inject, Logger } from '@nestjs/common';
|
||
|
import { constants } from 'fs/promises';
|
||
|
import { AssetCore, IAssetRepository, WithoutProperty, WithProperty } from '../asset';
|
||
|
import { usePagination } from '../domain.util';
|
||
|
import { IAssetJob, IBaseJob, IJobRepository, JobName, JOBS_ASSET_PAGINATION_SIZE } from '../job';
|
||
|
import { IStorageRepository } from '../storage';
|
||
|
|
||
|
export class MetadataService {
|
||
|
private logger = new Logger(MetadataService.name);
|
||
|
private assetCore: AssetCore;
|
||
|
|
||
|
constructor(
|
||
|
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
||
|
@Inject(IJobRepository) private jobRepository: IJobRepository,
|
||
|
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
|
||
|
) {
|
||
|
this.assetCore = new AssetCore(assetRepository, jobRepository);
|
||
|
}
|
||
|
|
||
|
async handleQueueSidecar(job: IBaseJob) {
|
||
|
try {
|
||
|
const { force } = job;
|
||
|
const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => {
|
||
|
return force
|
||
|
? this.assetRepository.getWith(pagination, WithProperty.SIDECAR)
|
||
|
: this.assetRepository.getWithout(pagination, WithoutProperty.SIDECAR);
|
||
|
});
|
||
|
|
||
|
for await (const assets of assetPagination) {
|
||
|
for (const asset of assets) {
|
||
|
const name = force ? JobName.SIDECAR_SYNC : JobName.SIDECAR_DISCOVERY;
|
||
|
await this.jobRepository.queue({ name, data: { asset } });
|
||
|
}
|
||
|
}
|
||
|
} catch (error: any) {
|
||
|
this.logger.error(`Unable to queue sidecar scanning`, error?.stack);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async handleSidecarSync(job: IAssetJob) {
|
||
|
const { asset } = job;
|
||
|
if (!asset.isVisible) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
const name = asset.type === AssetType.VIDEO ? JobName.EXTRACT_VIDEO_METADATA : JobName.EXIF_EXTRACTION;
|
||
|
await this.jobRepository.queue({ name, data: { asset } });
|
||
|
} catch (error: any) {
|
||
|
this.logger.error(`Unable to queue metadata extraction`, error?.stack);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async handleSidecarDiscovery(job: IAssetJob) {
|
||
|
let { asset } = job;
|
||
|
if (!asset.isVisible || asset.sidecarPath) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
const sidecarPath = `${asset.originalPath}.xmp`;
|
||
|
const exists = await this.storageRepository.checkFileExists(sidecarPath, constants.W_OK);
|
||
|
if (!exists) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
asset = await this.assetCore.save({ id: asset.id, sidecarPath });
|
||
|
// TODO: optimize to only queue assets with recent xmp changes
|
||
|
const name = asset.type === AssetType.VIDEO ? JobName.EXTRACT_VIDEO_METADATA : JobName.EXIF_EXTRACTION;
|
||
|
await this.jobRepository.queue({ name, data: { asset } });
|
||
|
} catch (error: any) {
|
||
|
this.logger.error(`Unable to queue metadata extraction: ${error}`, error?.stack);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|