* Process a ZIP archive: extract COLMAP files immediately, build index for images.
( archive: ArchiveReader, onProgress: (progress: ZipProgress) => void )
| 122 | * Process a ZIP archive: extract COLMAP files immediately, build index for images. |
| 123 | */ |
| 124 | async function processZipArchive( |
| 125 | archive: ArchiveReader, |
| 126 | onProgress: (progress: ZipProgress) => void |
| 127 | ): Promise<{ colmapFiles: Map<string, File>; imageIndex: Map<string, ArchiveEntry>; imageCount: number }> { |
| 128 | onProgress({ percent: 50, message: 'Reading archive contents...' }); |
| 129 | |
| 130 | // Get list of files in archive |
| 131 | const filesArray = await archive.getFilesArray(); |
| 132 | |
| 133 | onProgress({ percent: 55, message: 'Identifying COLMAP files...' }); |
| 134 | |
| 135 | // Separate COLMAP files and image files |
| 136 | const colmapEntries: Array<{ file: ArchiveEntry; path: string }> = []; |
| 137 | const splatEntries: Array<{ file: ArchiveEntry; path: string; size: number }> = []; |
| 138 | const imageIndex = new Map<string, ArchiveEntry>(); |
| 139 | let imageCount = 0; // Track actual unique images (imageIndex has duplicates for lookup) |
| 140 | |
| 141 | for (const entry of filesArray) { |
| 142 | const fullPath = buildArchiveEntryPath(entry.path, entry.file.name); |
| 143 | |
| 144 | if (isArchiveColmapPath(fullPath)) { |
| 145 | colmapEntries.push({ file: entry.file, path: fullPath }); |
| 146 | } else if (isArchiveSplatPath(fullPath)) { |
| 147 | splatEntries.push({ file: entry.file, path: fullPath, size: entry.file.size }); |
| 148 | } else if (isArchiveImagePath(fullPath)) { |
| 149 | // Store in index for lazy extraction |
| 150 | for (const key of getArchiveImageLookupKeys(fullPath)) { |
| 151 | if (!imageIndex.has(key)) { |
| 152 | imageIndex.set(key, entry.file); |
| 153 | } |
| 154 | } |
| 155 | imageCount++; // Count unique images (only the full path entry) |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | onProgress({ percent: 60, message: `Extracting ${colmapEntries.length} COLMAP files...` }); |
| 160 | |
| 161 | // Extract COLMAP files immediately |
| 162 | const colmapFiles = new Map<string, File>(); |
| 163 | |
| 164 | for (let i = 0; i < colmapEntries.length; i++) { |
| 165 | const entry = colmapEntries[i]; |
| 166 | const extractedFile = await entry.file.extract(); |
| 167 | |
| 168 | const filename = entry.path.split('/').pop() ?? entry.path; |
| 169 | const key = getColmapArchiveKey(entry.path); |
| 170 | |
| 171 | colmapFiles.set(key, extractedFile); |
| 172 | |
| 173 | const percent = 60 + Math.round(((i + 1) / colmapEntries.length) * 10); // 60-70% |
| 174 | onProgress({ percent, message: `Extracting ${filename}...` }); |
| 175 | } |
| 176 | |
| 177 | if (splatEntries.length > 0) { |
| 178 | const sortedSplatEntries = sortArchiveSplatCandidatesByPreference(splatEntries); |
| 179 | if (sortedSplatEntries.length === 0) { |
| 180 | throw new Error('Internal error selecting archive splat file'); |
| 181 | } |
no test coverage detected