( imageNames: string[], fetchImage: ImageFetchFunction, options: ImageZipExportOptions, onProgress?: ImageZipProgressCallback )
| 40 | } |
| 41 | |
| 42 | export async function exportImagesZip( |
| 43 | imageNames: string[], |
| 44 | fetchImage: ImageFetchFunction, |
| 45 | options: ImageZipExportOptions, |
| 46 | onProgress?: ImageZipProgressCallback |
| 47 | ): Promise<Blob> { |
| 48 | const { zipSync } = await import('fflate'); |
| 49 | |
| 50 | const totalImages = imageNames.length; |
| 51 | const zipData: Record<string, Uint8Array> = {}; |
| 52 | let processed = 0; |
| 53 | let failed = 0; |
| 54 | |
| 55 | for (const imageName of imageNames) { |
| 56 | try { |
| 57 | const file = await fetchImage(imageName); |
| 58 | if (!file) { |
| 59 | failed++; |
| 60 | processed++; |
| 61 | onProgress?.(Math.round((processed / totalImages) * 100), `Skipped: ${imageName}`); |
| 62 | continue; |
| 63 | } |
| 64 | |
| 65 | const jpegBlob = await convertToJpeg(file, options.jpegQuality); |
| 66 | const arrayBuffer = await jpegBlob.arrayBuffer(); |
| 67 | zipData[toJpegZipPath(imageName)] = new Uint8Array(arrayBuffer); |
| 68 | } catch (err) { |
| 69 | appLogger.warn(`[Image Export] Failed to process ${imageName}:`, err); |
| 70 | failed++; |
| 71 | } |
| 72 | |
| 73 | processed++; |
| 74 | onProgress?.(Math.round((processed / totalImages) * 100)); |
| 75 | } |
| 76 | |
| 77 | if (failed > 0) { |
| 78 | appLogger.warn(`[Image Export] ${failed}/${totalImages} images failed to export`); |
| 79 | } |
| 80 | |
| 81 | const zipped = zipSync(zipData, { level: 6 }); |
| 82 | return createZipBlob(zipped); |
| 83 | } |
| 84 | |
| 85 | export async function downloadImagesZip( |
| 86 | imageNames: string[], |
no test coverage detected