From 25a01e23450052f90c66d96da2c0bebec91ab3c6 Mon Sep 17 00:00:00 2001 From: sneak Date: Wed, 14 Oct 2026 04:15:47 +0000 Subject: [PATCH] Discard a cache whose list of what did not decrypt is damaged metadata.json's list of the albums and files the refresh could not decrypt is read entry by entry, so a value that is not a list of records discards the cache as other damage does, instead of failing every later backup. "Backup layout" now says that an album, or a file's copy in an album, that quak can no longer decrypt loses its folder, JSON or link as a deleted one does, and the originals stay. It no longer mentions the empty title in failures.json, which no test covers. Model: opus-5-5 --- README.md | 25 ++++++++++++++----------- src/library/store.ts | 7 ++++++- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index f371c1e..0a7e7a3 100644 --- a/README.md +++ b/README.md @@ -746,14 +746,14 @@ library's `lib.backup({ includeThumbnails: true })` also writes `thumbnails/.jpg` beside `collections/`; `quak backup` does not. A file in the backup's scope that the library could not decrypt (see "Default -reads vs. fresh reads") goes into `failures.json` with an empty title, since its -title is encrypted too, and an error that starts `could not be decrypted:` and -gives the reason. If a copy of it in another album decrypted, it does not: its -original is stored from that copy, and the album where it does not decrypt -neither links nor lists it. An album the library could not decrypt has no name -quak can read and no files it can list. A run without `--json` prints one line -for it, `FAILED album : could not be decrypted: `, and `--json` and -the `BackupResult` list its ID in `undecryptableAlbums`. Such a file in +reads vs. fresh reads") goes into `failures.json` with an error that starts +`could not be decrypted:` and gives the reason. If a copy of it in another album +decrypted, it does not: its original is stored from that copy, and the album +where it does not decrypt neither links nor lists it. An album the library could +not decrypt has no name quak can read and no files it can list. A run without +`--json` prints one line for it, +`FAILED album : could not be decrypted: `, and `--json` and the +`BackupResult` list its ID in `undecryptableAlbums`. Such a file in `failures.json`, or such an album, keeps the exit code at 1, run after run, until the server sends a copy that decrypts or deletes it; the next run then drops it. @@ -794,9 +794,12 @@ both, each named after the title with that file's extension (`IMG_0001.heic` and Each run removes the symlinks into the date folders that no longer belong in their collection's directory, and the directories (and JSON) of collections that -were deleted or renamed. Nothing else in `collections/` is touched: a file or a -symlink you put there stays, and a directory that still holds one after its -symlinks are removed stays too, with its JSON. +were deleted or renamed. An album quak can no longer decrypt loses its directory +and JSON the same way, and a file whose copy in an album quak can no longer +decrypt loses its symlink there and its entry in that album's JSON; the +originals stay at their save paths. Nothing else in `collections/` is touched: a +file or a symlink you put there stays, and a directory that still holds one +after its symlinks are removed stays too, with its JSON. Each file is downloaded exactly once regardless of how many collections it appears in, and written once: straight to its save path, with no copy left in diff --git a/src/library/store.ts b/src/library/store.ts index dabb166..fc59b78 100644 --- a/src/library/store.ts +++ b/src/library/store.ts @@ -119,7 +119,12 @@ export class MetadataStore { }; store.files.set(fileKey(file.collectionID, file.id), file); } - store.undecryptable = parsed.undecryptable ?? []; + for (const record of parsed.undecryptable) { + if (typeof record.collectionID !== "number") { + throw new Error("damaged record of what did not decrypt"); + } + store.undecryptable.push(record); + } } catch { // Any corruption discards the partial result: a half-read cache is // worse than an empty one, since the refresh unit will rebuild it.