diff --git a/package-lock.json b/package-lock.json
index 41055b4..9ab50c1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19,6 +19,7 @@
"cl-editor": "^2.3.0",
"daisyui": "^4.12.10",
"dayjs": "^1.11.12",
+ "file-saver": "^2.0.5",
"firebase": "^10.12.5",
"jszip": "^3.10.1",
"p-limit": "^6.1.0",
@@ -39,6 +40,7 @@
"@sveltejs/kit": "^2.5.20",
"@sveltejs/vite-plugin-svelte": "^3.1.1",
"@types/byte-size": "^8.1.2",
+ "@types/file-saver": "^2.0.7",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
@@ -2047,6 +2049,12 @@
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
},
+ "node_modules/@types/file-saver": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.7.tgz",
+ "integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==",
+ "dev": true
+ },
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -5304,6 +5312,11 @@
"node": "^10.12.0 || >=12.0.0"
}
},
+ "node_modules/file-saver": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
+ "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
+ },
"node_modules/filesize": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-6.4.0.tgz",
diff --git a/package.json b/package.json
index ee92f72..94c659c 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"@sveltejs/kit": "^2.5.20",
"@sveltejs/vite-plugin-svelte": "^3.1.1",
"@types/byte-size": "^8.1.2",
+ "@types/file-saver": "^2.0.7",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
@@ -52,6 +53,7 @@
"cl-editor": "^2.3.0",
"daisyui": "^4.12.10",
"dayjs": "^1.11.12",
+ "file-saver": "^2.0.5",
"firebase": "^10.12.5",
"jszip": "^3.10.1",
"p-limit": "^6.1.0",
diff --git a/src/components/photoAlbum/PhotoAlbumViewer.svelte b/src/components/photoAlbum/PhotoAlbumViewer.svelte
index b528cc7..1e3653a 100644
--- a/src/components/photoAlbum/PhotoAlbumViewer.svelte
+++ b/src/components/photoAlbum/PhotoAlbumViewer.svelte
@@ -20,6 +20,10 @@
import Time from "svelte-time/Time.svelte"
import BiggerPictureThumbnails from "./thumbnails.svelte"
+ import pkg from "file-saver"
+ import pLimit from "p-limit"
+ const { saveAs } = pkg
+
export let photoAlbum: PhotoAlbum
export let preview = false
@@ -70,29 +74,41 @@
})
}
+ // -- Download --
+ let downloading = false
+ let amountFinished = 0
+
async function downloadHandler() {
+ ;(document.activeElement as HTMLElement).blur()
+ downloading = true
+ amountFinished = 0
const zip = new JSZip()
- const imgFolder = zip.folder("images")
- if (!imgFolder) throw new Error("No image folder")
-
- for (const [index, url] of photoAlbum.imageUrls.entries()) {
- const response = await fetch(url)
- const blob = await response.blob()
- imgFolder.file(`image${index + 1}.jpg`, blob)
- }
+ const limit = pLimit(4)
+
+ await Promise.all(
+ photoAlbum.imageUrls.map(async (url, index) => {
+ return limit(async () => {
+ const response = await fetch(url)
+ const blob = await response.blob()
+ amountFinished++
+ zip.file(`image${index + 1}.webp`, blob)
+ })
+ }),
+ )
const content = await zip.generateAsync({ type: "blob" })
- saveAs(content, "images.zip")
+ saveAs(content, `${photoAlbum.title}.zip`)
+ downloading = false
}
- function saveAs(blob: Blob, name: string) {
- var a = document.createElement("a")
- document.body.append(a)
- a.download = name
- a.href = URL.createObjectURL(blob)
- a.click()
- a.remove()
- }
+ onMount(() => {
+ // Prevent user accidentally leaving page when album is downloading
+ window.addEventListener("beforeunload", (e) => {
+ if (downloading) {
+ e.preventDefault()
+ }
+ })
+ })