You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: internal/photoprism/batch/README.md
+9-4Lines changed: 9 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
## PhotoPrism — Batch Edit Package
2
2
3
-
**Last Updated:** November 19, 2025
3
+
**Last Updated:** November 20, 2025
4
4
5
5
### Overview
6
6
@@ -35,8 +35,9 @@ The `internal/photoprism/batch` package implements the form schema (`PhotosForm`
35
35
3. The handler always reuses the ordered `search.BatchPhotos` results when serializing the `models` array so every response mirrors the original selection and exposes the full `search.Photo` schema (thumbnail hashes, files, etc.) required by the lightbox.
36
36
4. After persisting updates, the handler issues a follow-up `query.PhotoPreloadByUIDs` call so `batch.PrepareAndSavePhotos` gets hydrated entities for album/label mutations without disrupting the frontend-facing payload.
37
37
5.`batch.PrepareAndSavePhotos` iterates over the preloaded entities, applies requested album/label changes, builds `PhotoSaveRequest` instances via `batch.NewPhotoSaveRequest`, and persists the updates before returning a summary (requests, results, updated count, `MutationStats`) to the API layer.
38
-
6.`SavePhotos` (invoked by the helper) loops once per request, updates only the columns that changed, clears `checked_at`, touches `edited_at`, and queues `entity.UpdateCountsAsync()` once if any photo saved.
39
-
7. Refreshed models and values are sent back in the response form so the frontend can merge and display the changes, and the mutation stats drive the production log line (`updated photo metadata (1/3) and labels (3/3)`) so operators can see which parts of the request succeeded even when metadata columns remained untouched.
38
+
6.`resolveBatchItemValues` runs before per-photo work so album/label additions referenced by title are looked up or created once per batch (rather than per photo) and deleted albums/labels are restored before use.
39
+
7.`SavePhotos` (invoked by the helper) loops once per request, updates only the columns that changed, clears `checked_at`, touches `edited_at`, and queues `entity.UpdateCountsAsync()` once if any photo saved. When album mutations occurred and YAML backups are enabled, the resolved album list is written back to disk via `updateAlbumBackups` after all database work succeeds.
40
+
8. Refreshed models and values are sent back in the response form so the frontend can merge and display the changes, and the mutation stats drive the production log line (`updated photo metadata (1/3) and labels (3/3)`) so operators can see which parts of the request succeeded even when metadata columns remained untouched.
40
41
41
42
### Batch Edit API Endpoint
42
43
@@ -154,7 +155,7 @@ Each field embeds one of the typed wrappers (`String`, `Bool`, `Time`, `Int`, et
154
155
155
156
-`Action` enums (`none`, `update`, `add`, `remove`) describe intent. Strings treat `remove` the same as `update` plus empty values, allowing the backend to wipe titles/captions clean.
156
157
- Source columns (`TitleSrc`, `CaptionSrc`, `TypeSrc`, `PlaceSrc`, details `*_src`) keep track of provenance. `SavePhotos` updates them whenever batch edits win over prior metadata (EXIF, AI, manual, etc.).
157
-
- Album & label updates respect UID validation: `ApplyAlbums` verifies `PhotoUID` / `AlbumUID`, creates albums by title when needed, and delegates to `entity.AddPhotoToAlbums`, which now uses per-album keyed locks to avoid blocking unrelated requests.
158
+
- Album & label updates respect UID validation: `ApplyAlbums` verifies `PhotoUID` / `AlbumUID`, creates albums by title when needed, and delegates to `entity.AddPhotoToAlbums`, which now uses per-album keyed locks to avoid blocking unrelated requests.`Items.ResolveValuesByTitle` plus `resolveBatchItemValues` ensure those creations happen once per batch, so per-photo calls operate on cached UIDs instead of repeating lookups.
158
159
- Label writes reuse existing `PhotoLabel` rows when possible, force 100 % confidence for manual/batch additions, and demote AI suggestions by setting `uncertainty = 100` when users explicitly remove them.
159
160
- Keyword keywords stay consistent because label removals call `photo.RemoveKeyword` and `SaveDetails` immediately, while location edits append unique place keywords via `txt.UniqueWords`.
-`internal/photoprism/batch/datelogic_test.go` ensures cross-field dependencies (local time vs. UTC) stay consistent.
221
222
-`internal/photoprism/batch/save_test.go` exercises partial updates, detail edits, `CheckedAt` resets, and the `PreparePhotoSaveRequests` / `PrepareAndSavePhotos` helpers.
222
223
-`internal/api/batch_photos_edit_test.go` provides end-to-end coverage for response envelopes (`SuccessNoChange`, `SuccessRemoveValues`, etc.).
224
+
-`internal/photoprism/batch/save_resolve_test.go` validates pre-resolution helpers for albums/labels, while `save_backup_test.go` covers the YAML backup flow controlled by `updateAlbumBackups`.
223
225
-**Logging**
224
226
- The package uses the shared `event.Log` logger. Debug logs trace selections, album/label changes, and dirty-field sets; warnings/errors surface failed queries so operators can inspect database health. The final `INFO` line now reports metadata success counts alongside album and label mutations (including error tallies) so label-only edits no longer read as “0 out of N photos”.
-`save_photo.go` — `savePhoto` applies a single request, compares old/new values, and writes only the changed columns (indirectly invoked by `SavePhotos`).
246
+
-`save_resolve.go` — album/label title resolution helpers that run before persistence so per-photo work only receives resolved UIDs.
247
+
-`save_backup.go` — YAML backup synchronisation for albums whenever batch edits touch them and backups are enabled.
243
248
-`datelogic.go` — helpers for reconciling time zones and date parts when the UI only supplies partial values.
0 commit comments