Keep using Photos.app like you always do. Attic quietly backs up your originals and edits to an S3 bucket you control. One-way, append-only.
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

Show asset context on export errors (cloud state, filename, size)

+21 -3
+21 -3
cli/src/commands/backup.ts
··· 2 2 import { 3 3 AssetKind, 4 4 buildMetadataJson, 5 + CloudLocalState, 5 6 extensionFromUtiOrFilename, 6 7 metadataKey, 7 8 originalKey, ··· 180 181 async function uploadExported( 181 182 batchResult: ExportBatchResult, 182 183 ): Promise<void> { 183 - // Record export errors 184 + // Record export errors with asset context 184 185 for (const err of batchResult.errors) { 185 - console.error(` Export error: ${err.uuid} — ${err.message}`); 186 + const asset = assetByUuid.get(err.uuid); 187 + const detail = asset 188 + ? exportErrorDetail(asset, err.message) 189 + : err.message; 190 + console.error(` Export error: ${assetLabel(err.uuid)} — ${detail}`); 186 191 report.errors.push(err); 187 192 report.failed++; 188 - logger.error(err.uuid, err.message); 193 + logger.error(err.uuid, detail); 189 194 } 190 195 191 196 for (const exported of batchResult.results) { ··· 450 455 } 451 456 452 457 return report; 458 + } 459 + 460 + /** Add context to a ladder export error based on what we know about the asset. */ 461 + function exportErrorDetail(asset: PhotoAsset, message: string): string { 462 + const hints: string[] = []; 463 + if (asset.cloudLocalState === CloudLocalState.ICLOUD_ONLY) { 464 + hints.push("asset is iCloud-only (original not downloaded locally)"); 465 + } 466 + if (!asset.originalFileSize) { 467 + hints.push("no original file size recorded"); 468 + } 469 + if (hints.length === 0) return message; 470 + return `${message} — ${hints.join(", ")}`; 453 471 } 454 472 455 473 function contentTypeFor(ext: string): string {