diff --git a/server/apps/immich/src/api-v1/album/album.controller.ts b/server/apps/immich/src/api-v1/album/album.controller.ts index c890f4ae57..d6cb5809e7 100644 --- a/server/apps/immich/src/api-v1/album/album.controller.ts +++ b/server/apps/immich/src/api-v1/album/album.controller.ts @@ -121,8 +121,9 @@ export class AlbumController { @Param('albumId', new ParseUUIDPipe({ version: '4' })) albumId: string, @Response({ passthrough: true }) res: Res, ): Promise { - const { stream, filename } = await this.albumService.downloadArchive(authUser, albumId); + const { stream, filename, filesize } = await this.albumService.downloadArchive(authUser, albumId); res.attachment(filename); + res.setHeader('X-Immich-Content-Length-Hint', filesize); return stream; } } diff --git a/server/apps/immich/src/api-v1/album/album.service.ts b/server/apps/immich/src/api-v1/album/album.service.ts index 097cac6f2b..9c48e1c5a7 100644 --- a/server/apps/immich/src/api-v1/album/album.service.ts +++ b/server/apps/immich/src/api-v1/album/album.service.ts @@ -171,11 +171,13 @@ export class AlbumService { try { const archive = archiver('zip', { store: true }); const stream = new StreamableFile(archive); + let totalSize = 0; for (const { assetInfo } of album.assets) { const { originalPath } = assetInfo; const name = `${assetInfo.exifInfo?.imageName || assetInfo.id}${extname(originalPath)}`; archive.file(originalPath, { name }); + totalSize += Number(assetInfo.exifInfo?.fileSizeInByte || 0); } archive.finalize(); @@ -183,6 +185,7 @@ export class AlbumService { return { stream, filename: `${album.albumName}.zip`, + filesize: totalSize, }; } catch (e) { Logger.error(`Error downloading album ${e}`, 'downloadArchive'); diff --git a/web/src/lib/components/album-page/album-viewer.svelte b/web/src/lib/components/album-page/album-viewer.svelte index 413ca2f068..92a92898da 100644 --- a/web/src/lib/components/album-page/album-viewer.svelte +++ b/web/src/lib/components/album-page/album-viewer.svelte @@ -322,8 +322,20 @@ $downloadAssets[fileName] = 0; + let total = 0; const { data, status } = await api.albumApi.downloadArchive(album.id, { - responseType: 'blob' + responseType: 'blob', + onDownloadProgress: function (progressEvent) { + const request = this as XMLHttpRequest; + if (!total) { + total = Number(request.getResponseHeader('X-Immich-Content-Length-Hint')) || 0; + } + + if (total) { + const current = progressEvent.loaded; + $downloadAssets[fileName] = Math.floor((current / total) * 100); + } + } }); if (!(data instanceof Blob)) {