mirror of
https://github.com/immich-app/immich
synced 2026-08-15 13:03:57 +00:00
feat: workflow asset tag trigger/filter/action (#29043)
* feat: workflow asset tag trigger and filter * feat(web): show tag names in workflow editor * fix(web): tag picker in schema config editor * fix: invalid plugin manifest * chore: update workflow method wrapper type * chore: update tag filter method declaration * feat: workflow action to add tags to assets
This commit is contained in:
parent
b82d480552
commit
a939561e70
17 changed files with 175 additions and 8 deletions
|
|
@ -43,7 +43,7 @@ type EventMap = {
|
|||
|
||||
// asset events
|
||||
AssetCreate: [{ asset: Pick<Asset, 'id' | 'ownerId'>; file?: UploadFile }];
|
||||
AssetTag: [{ assetId: string }];
|
||||
AssetTag: [{ assetId: string; userId: string }];
|
||||
AssetUntag: [{ assetId: string }];
|
||||
AssetHide: [{ assetId: string; userId: string }];
|
||||
AssetShow: [{ assetId: string; userId: string }];
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { WorkflowSearchDto } from 'src/dtos/workflow.dto';
|
|||
import { DB } from 'src/schema';
|
||||
import { WorkflowStepTable } from 'src/schema/tables/workflow-step.table';
|
||||
import { WorkflowTable } from 'src/schema/tables/workflow.table';
|
||||
import { withTags } from 'src/utils/database';
|
||||
|
||||
export type WorkflowStepUpsert = Omit<Insertable<WorkflowStepTable>, 'workflowId' | 'order'>;
|
||||
|
||||
|
|
@ -143,6 +144,7 @@ export class WorkflowRepository {
|
|||
.leftJoin('asset_exif', 'asset_exif.assetId', 'asset.id')
|
||||
.select((eb) => [
|
||||
...columns.workflowAssetV1,
|
||||
withTags,
|
||||
jsonObjectFrom(
|
||||
eb
|
||||
.selectFrom('asset_exif')
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ export class TagService extends BaseService {
|
|||
const results = await this.tagRepository.upsertAssetIds(items);
|
||||
for (const assetId of new Set(results.map((item) => item.assetId))) {
|
||||
await this.updateTags(assetId);
|
||||
await this.eventRepository.emit('AssetTag', { assetId });
|
||||
await this.eventRepository.emit('AssetTag', { assetId, userId: auth.user.id });
|
||||
}
|
||||
|
||||
return { count: results.length };
|
||||
|
|
@ -114,7 +114,7 @@ export class TagService extends BaseService {
|
|||
}
|
||||
|
||||
await this.updateTags(assetId);
|
||||
await this.eventRepository.emit('AssetTag', { assetId });
|
||||
await this.eventRepository.emit('AssetTag', { assetId, userId: auth.user.id });
|
||||
}
|
||||
|
||||
return results;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { AlbumsAddAssetsDto, CreateAlbumDto, GetAlbumsDto } from 'src/dtos/album
|
|||
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { PluginManifestDto } from 'src/dtos/plugin-manifest.dto';
|
||||
import { TagBulkAssetsDto } from 'src/dtos/tag.dto';
|
||||
import {
|
||||
BootstrapEventPriority,
|
||||
DatabaseLock,
|
||||
|
|
@ -27,6 +28,7 @@ import { ArgOf } from 'src/repositories/event.repository';
|
|||
import { AlbumService } from 'src/services/album.service';
|
||||
import { AssetService } from 'src/services/asset.service';
|
||||
import { BaseService } from 'src/services/base.service';
|
||||
import { TagService } from 'src/services/tag.service';
|
||||
import { JobOf } from 'src/types';
|
||||
|
||||
const dummy = () => {
|
||||
|
|
@ -69,6 +71,7 @@ export class WorkflowExecutionService extends BaseService {
|
|||
this.jwtSecret = this.cryptoRepository.randomBytesAsText(32);
|
||||
|
||||
const albumService = BaseService.create(AlbumService, this);
|
||||
const tagService = BaseService.create(TagService, this);
|
||||
|
||||
const searchAlbums = this.wrap<[dto: GetAlbumsDto]>((authDto, ctx, args) => albumService.getAll(authDto, ...args));
|
||||
const createAlbum = this.wrap<[dto: CreateAlbumDto]>((authDto, ctx, args) => albumService.create(authDto, ...args));
|
||||
|
|
@ -106,6 +109,9 @@ export class WorkflowExecutionService extends BaseService {
|
|||
|
||||
throw new Error('Hostname did not match any listed in methods[].allowedHosts in the plugin manifest');
|
||||
});
|
||||
const bulkTagAssets = this.wrap<[dto: TagBulkAssetsDto]>((authDto, ctx, args) =>
|
||||
tagService.bulkTagAssets(authDto, ...args),
|
||||
);
|
||||
|
||||
const functions = {
|
||||
searchAlbums,
|
||||
|
|
@ -113,6 +119,7 @@ export class WorkflowExecutionService extends BaseService {
|
|||
addAssetsToAlbum,
|
||||
addAssetsToAlbums,
|
||||
httpRequest,
|
||||
bulkTagAssets,
|
||||
};
|
||||
|
||||
const stubs: typeof functions = {
|
||||
|
|
@ -121,6 +128,7 @@ export class WorkflowExecutionService extends BaseService {
|
|||
addAssetsToAlbum: dummy,
|
||||
addAssetsToAlbums: dummy,
|
||||
httpRequest: dummy,
|
||||
bulkTagAssets: dummy,
|
||||
};
|
||||
|
||||
const plugins = await this.pluginRepository.getForLoad();
|
||||
|
|
@ -309,6 +317,11 @@ export class WorkflowExecutionService extends BaseService {
|
|||
return this.onAssetTrigger({ userId, assetId, trigger: WorkflowTrigger.AssetMetadataExtraction });
|
||||
}
|
||||
|
||||
@OnEvent({ name: 'AssetTag' })
|
||||
onAssetTagged({ assetId, userId }: ArgOf<'AssetTag'>) {
|
||||
return this.onAssetTrigger({ userId, assetId, trigger: WorkflowTrigger.AssetTagged });
|
||||
}
|
||||
|
||||
private async onAssetTrigger({ userId, assetId, trigger }: AssetTrigger) {
|
||||
const items = await this.workflowRepository.search({ userId, trigger });
|
||||
await this.jobRepository.queueAll(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ export const triggerMap: Record<WorkflowTrigger, WorkflowType[]> = {
|
|||
[WorkflowTrigger.AssetCreate]: [WorkflowType.AssetV1],
|
||||
// [WorkflowTrigger.PersonRecognized]: [WorkflowType.AssetPersonV1],
|
||||
[WorkflowTrigger.AssetMetadataExtraction]: [WorkflowType.AssetV1],
|
||||
[WorkflowTrigger.AssetTagged]: [WorkflowType.AssetV1],
|
||||
};
|
||||
|
||||
export const getWorkflowTriggers = () =>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue