1 Commits
Author SHA1 Message Date
sneak 820a22f8cb --verify keeps a mismatched original until a matching copy replaces it (closes #199)
check / check (push) Canceled after 0s
With --verify, an original whose hash does not match is no longer removed
before its replacement is downloaded. replaceOriginal, beside placeOriginal
in src/library/content.ts, has the content cache's new fetchOriginal write
the replacement to a temporary file beside the original, hashes it, and
renames it over the original only when it matches; a live photo's image and
video are both hashed before either is renamed. A failed or mismatching
download leaves the old bytes and album links, removes the temporary files,
and lands in failures.json. The replacement is always downloaded, never
copied from the cache. The leftover-file cleanup also takes a live photo's
replacement files, .quak-<pid>-<random>.<ext>.

Model: opus-5-5
2026-10-16 03:38:17 +00:00
2 changed files with 12 additions and 18 deletions

No files matched your search

+8 -11
View File
@@ -887,17 +887,14 @@ own, hashed the same way, and renamed over the original only when it matches; a
live photo's image and video are both hashed before either is renamed. Until live photo's image and video are both hashed before either is renamed. Until
then the original stays as it was: when the download fails, or what it brings then the original stays as it was: when the download fails, or what it brings
does not match either, the temporary files are deleted, the original keeps its does not match either, the temporary files are deleted, the original keeps its
bytes and its album links, and the file goes into `failures.json`. A run that bytes and its album links, and the file goes into `failures.json`. A file whose
stops between renaming a live photo's new image and its new video leaves the new metadata records no hash is left as it is and counted as unchecked. A stored
image beside the old video, which a later `--verify` run replaces if the video original that cannot be read is left as it is and counts as failed. The summary
was the part that did not match. A file whose metadata records no hash is left and `--json` add the counts `verified`, `mismatched` and `unchecked`, all of
as it is and counted as unchecked. A stored original that cannot be read is left originals that were already stored; one first downloaded in this run is in none
as it is and counts as failed. The summary and `--json` add the counts of them. A mismatch that was replaced with matching bytes does not make the exit
`verified`, `mismatched` and `unchecked`, all of originals that were already code non-zero. Without `--verify` nothing is hashed, the summary is unchanged,
stored; one first downloaded in this run is in none of them. A mismatch that was and the three counts are 0 in `--json`.
replaced with matching bytes does not make the exit code non-zero. Without
`--verify` nothing is hashed, the summary is unchanged, and the three counts are
0 in `--json`.
Each original is written to a temporary file in the same directory, synced to Each original is written to a temporary file in the same directory, synced to
disk, and renamed into place, so an original is either complete or absent, even disk, and renamed into place, so an original is either complete or absent, even
+4 -7
View File
@@ -13,8 +13,6 @@
// originals, the thumbnail pool for thumbnails. The pool limits concurrency, // originals, the thumbnail pool for thumbnails. The pool limits concurrency,
// orders on-demand work ahead of background, and dedups by key so a fileID // orders on-demand work ahead of background, and dedups by key so a fileID
// requested twice while the first is still in flight downloads once. // requested twice while the first is still in flight downloads once.
// `fetchOriginal` passes no key, so it is not shared with a fetch of the same
// file already in flight.
// //
// Integrity. The reused streaming decrypt is the enforced guarantee: every // Integrity. The reused streaming decrypt is the enforced guarantee: every
// chunk is authenticated and the writer renames the file into place only once // chunk is authenticated and the writer renames the file into place only once
@@ -441,11 +439,10 @@ export const placeOriginal = async (
// copy there, a live photo as its image and its video beside that file, each // copy there, a live photo as its image and its video beside that file, each
// with its own extension. Only a copy that matches is renamed over the stored // with its own extension. Only a copy that matches is renamed over the stored
// one, and a live photo's image and video are both hashed before either is // one, and a live photo's image and video are both hashed before either is
// renamed. The image is renamed first: should the run stop, or the video's // renamed. Two renames cannot happen at once: should the video's fail, the
// rename fail, before the video is renamed, the new image is left beside the // image is already the new one, and the next backup with `verify` finds that
// old video, and a later backup with `verify` replaces the video if the video // the two do not match. When `get` fails or the copy does not match, the
// was the part that did not match. When `get` fails or the copy does not match, // temporary files are removed and the stored original stays as it was.
// the temporary files are removed and the stored original stays as it was.
export const replaceOriginal = async ( export const replaceOriginal = async (
root: string, root: string,
file: EnteFile, file: EnteFile,