feat: workflow filter by missing EXIF property (#32713)

Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
(cherry picked from commit a1a80cb1c416d3a480963a78d7b9df329d867bf6)
This commit is contained in:
austinmdillow
2026-10-28 20:41:41 +03:00
committed by Codex
parent 32e02a05d2
commit dc5553b239
3 changed files with 129 additions and 10 deletions
+2 -2
View File
@@ -407,7 +407,7 @@
"matchType": {
"type": "string",
"title": "Match type",
"enum": ["contains", "startsWith", "exact", "regex"],
"enum": ["contains", "startsWith", "exact", "regex", "present", "missing"],
"default": "contains",
"description": "Type of pattern matching to perform",
"uiHint": {
@@ -424,7 +424,7 @@
}
}
},
"required": ["property", "pattern"]
"required": ["property"]
},
"uiHints": ["Filter"]
},
+21 -8
View File
@@ -3,13 +3,18 @@ import { AssetVisibility } from '@immich/sdk';
import type { Manifest } from '../dist/index.d.ts';
type MatchValueConfig = {
pattern: string;
matchType?: 'contains' | 'exact' | 'regex' | 'startsWith';
pattern?: string;
matchType?: 'contains' | 'exact' | 'regex' | 'startsWith' | 'missing' | 'present';
caseSensitive?: boolean;
};
const matchValueResult = (value: string, config: MatchValueConfig) => {
const { pattern, matchType = 'contains', caseSensitive = false } = config;
const matchValueResult = (value: string | null, config: MatchValueConfig) => {
const { pattern = '', matchType = 'contains', caseSensitive = false } = config;
if (value === null) {
return { workflow: { continue: matchType === 'missing' } };
}
const searchName = caseSensitive ? value : value.toLowerCase();
const searchPattern = caseSensitive ? pattern : pattern.toLowerCase();
@@ -32,6 +37,15 @@ const matchValueResult = (value: string, config: MatchValueConfig) => {
return { workflow: { continue: regex.test(value) } };
}
case 'present': {
return { workflow: { continue: true } };
}
case 'missing': {
// value is not null at this point
return { workflow: { continue: false } };
}
default: {
return {};
}
@@ -135,11 +149,10 @@ const methods = wrapper<Manifest>({
},
assetExifFilter: ({ config, data }) => {
if (!data.asset.exifInfo || data.asset.exifInfo[config.property] === null) {
return { workflow: { continue: false } };
}
let value = data.asset.exifInfo?.[config.property];
return matchValueResult(String(data.asset.exifInfo[config.property]), config);
// Empty EXIF value is treated as missing to mirror behavior of the web UI
return matchValueResult(value === null || value === undefined || value === '' ? null : String(value), config);
},
assetDateFilter: ({ config, data }) => {