feat: pre-zip download all and photo replacement by name (#312, #313)

Pre-zip downloads:
- Generate ZIP in background after photo mutations (upload/delete/watermark change)
- Serve cached zip with Content-Length for instant downloads and native progress bar
- Falls back to on-the-fly streaming when no cache exists yet
- Frontend uses browser-native download when zip is ready (no blob buffering)
- New downloadZipService with debounced regeneration and in-memory locking

Photo replacement:
- Admin upload form gets "Replace existing photos with same name" checkbox
- Matches by original_filename (case-insensitive) within the same event
- Preserves photo ID, position, feedback, category, and visibility
- Updates file, thumbnail, dimensions, EXIF capture date on replacement
- Ambiguous matches (multiple photos with same name) skip replacement with warning
- New photoReplacementService with findReplacementCandidate and replacePhoto
This commit is contained in:
Paul Nothaft
2026-04-23 16:49:31 +02:00
parent 4353acebf9
commit e18afd3e6b
16 changed files with 664 additions and 36 deletions
+2 -1
View File
@@ -67,7 +67,8 @@ export const useDownloadPhoto = () => {
export const useDownloadAllPhotos = () => {
return useMutation({
mutationFn: (slug: string) => galleryService.downloadAllPhotos(slug),
mutationFn: ({ slug, zipReady }: { slug: string; zipReady?: boolean }) =>
galleryService.downloadAllPhotos(slug, zipReady),
onSuccess: () => {
toast.success('Download started');
},