From 4f5289109511c562a05f1c934f6510a2fd14ff20 Mon Sep 17 00:00:00 2001 From: Ben Beckford Date: Mon, 29 Jun 2026 07:14:06 -0700 Subject: [PATCH] feat: workflow action to add tags to assets --- packages/plugin-core/manifest.json | 22 +++++++++++++++++++ packages/plugin-core/src/index.ts | 7 ++++++ packages/plugin-sdk/src/host-functions.ts | 9 ++++++++ .../services/workflow-execution.service.ts | 8 +++++++ 4 files changed, 46 insertions(+) diff --git a/packages/plugin-core/manifest.json b/packages/plugin-core/manifest.json index 29a79041c9..27870322d0 100644 --- a/packages/plugin-core/manifest.json +++ b/packages/plugin-core/manifest.json @@ -203,6 +203,28 @@ }, "uiHints": ["Filter"] }, + { + "name": "assetAddTags", + "title": "Add Tags", + "description": "Add tags to an asset", + "types": ["AssetV1"], + "hostFunctions": true, + "schema": { + "type": "object", + "properties": { + "tags": { + "title": "Tags", + "description": "Tags to add to the asset", + "type": "string", + "array": true, + "uiHint": { + "type": "TagId" + } + } + }, + "required": ["tags"] + } + }, { "name": "assetTagFilter", "title": "Filter by tags", diff --git a/packages/plugin-core/src/index.ts b/packages/plugin-core/src/index.ts index 526c9586fb..8e548bbbc3 100644 --- a/packages/plugin-core/src/index.ts +++ b/packages/plugin-core/src/index.ts @@ -3,6 +3,11 @@ import { AssetVisibility } from '@immich/sdk'; import type { Manifest } from '../dist/index.d.ts'; const methods = wrapper({ + assetAddTags: ({ config, data, functions }) => { + functions.bulkTagAssets({ assetIds: [data.asset.id], tagIds: config.tags }); + return {}; + }, + assetAddToAlbums: ({ config, data, functions }) => { const assetId = data.asset.id; @@ -188,6 +193,7 @@ const methods = wrapper({ }); const { + assetAddTags, assetAddToAlbums, assetArchive, assetFavorite, @@ -205,6 +211,7 @@ const { } = methods; export { + assetAddTags, assetAddToAlbums, assetArchive, assetFavorite, diff --git a/packages/plugin-sdk/src/host-functions.ts b/packages/plugin-sdk/src/host-functions.ts index 9d52630438..e0b6e34da9 100644 --- a/packages/plugin-sdk/src/host-functions.ts +++ b/packages/plugin-sdk/src/host-functions.ts @@ -4,6 +4,8 @@ import { type BulkIdResponseDto, type BulkIdsDto, type CreateAlbumDto, + type TagBulkAssetsDto, + type TagBulkAssetsResponseDto, } from '@immich/sdk'; declare module 'extism:host' { @@ -47,6 +49,7 @@ export const availableFunctions = [ 'addAssetsToAlbum', 'addAssetsToAlbums', 'httpRequest', + 'bulkTagAssets', ] as const; export const hostFunctions = (authToken: string) => { @@ -96,5 +99,11 @@ export const hostFunctions = (authToken: string) => { authToken, [url, options], ), + bulkTagAssets: (dto: TagBulkAssetsDto) => + call<[TagBulkAssetsDto], TagBulkAssetsResponseDto>( + 'bulkTagAssets', + authToken, + [dto], + ), } satisfies Record<(typeof availableFunctions)[number], unknown>; }; diff --git a/server/src/services/workflow-execution.service.ts b/server/src/services/workflow-execution.service.ts index 3a7148cdcb..0732b548fe 100644 --- a/server/src/services/workflow-execution.service.ts +++ b/server/src/services/workflow-execution.service.ts @@ -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)); @@ -105,6 +108,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, @@ -112,6 +118,7 @@ export class WorkflowExecutionService extends BaseService { addAssetsToAlbum, addAssetsToAlbums, httpRequest, + bulkTagAssets, }; const stubs: typeof functions = { @@ -120,6 +127,7 @@ export class WorkflowExecutionService extends BaseService { addAssetsToAlbum: dummy, addAssetsToAlbums: dummy, httpRequest: dummy, + bulkTagAssets: dummy, }; const plugins = await this.pluginRepository.getForLoad();