diff --git a/README.md b/README.md index fe40038..5654819 100644 --- a/README.md +++ b/README.md @@ -788,7 +788,8 @@ subsequent runs, existing originals are skipped. If a download fails, the error is logged and the backup continues with the next file. The exit code is non-zero if any files failed. `quak backup` opens its library with the thumbnail and originals precache off, so the only file content it fetches is the originals the -backup stores. +backup stores. It also opens it with no background timer, so it asks the server +for changes only as it starts, not again while it downloads. With `--verify`, or `lib.backup({ verify: true })`, a run also hashes each original already at its save path the way a download is checked (see "On-disk @@ -869,7 +870,7 @@ background, so an unreachable server does not block opening. | `client` | required | the account client (a `Client`, or any `LibraryClient`) | | `cacheDirectory` | `quak/` under the per-user cache directory | where `metadata.json` and the content cache live | | `downloadDirectory` | `photos` in the working directory | root of the save paths; an original stored there counts as cached | -| `refreshIntervalSeconds` | `3` | background refresh cadence | +| `refreshIntervalSeconds` | `3` | background refresh cadence; `Infinity` for no background timer | | `precacheThumbnails` | `true` | prefetch every thumbnail, newest first | | `precacheOriginals` | `true` | prefetch the favorites album and the latest-window originals | | `precacheOriginalsDays` | `7` | length in days of that latest window | diff --git a/TODO.md b/TODO.md index 87de288..25a6bfb 100644 --- a/TODO.md +++ b/TODO.md @@ -25,6 +25,13 @@ declares one. # Completed Steps +- 2026-10-14: `quak backup` no longer asks the server for changes every 3 + seconds while it runs (issue 183). It opens its library with + `refreshIntervalSeconds: Infinity`, which the library now takes as no + background timer, so the backup refreshes only as it starts and no timed + refresh starts another ML data pass during the run. A test holds a backup's + download open while two hours pass and counts its `/collections/v2` requests. + - 2026-10-14: `test/cli/backup-end-to-end.test.ts` runs `quak backup` as cron does (issue 186): `backupCommand` and `run`, the real `loadSession` reading a session saved after logging in, and every request answered by a fake of the diff --git a/src/cli-commands.ts b/src/cli-commands.ts index 5f8b0b6..154c180 100644 --- a/src/cli-commands.ts +++ b/src/cli-commands.ts @@ -427,10 +427,13 @@ export const backupCommand = async ( try { // The precache is off: the backup fetches what it needs, and must not // also fill the cache with every thumbnail and the recent originals. + // No refresh starts on a timer: the backup lists its files once, from + // the refresh it waits for, and can run for hours. const lib = await Library.open({ client, downloadDirectory: dir, cacheDirectory: ctx.cacheDir, + refreshIntervalSeconds: Infinity, precacheThumbnails: false, precacheOriginals: false, }); diff --git a/src/library/index.ts b/src/library/index.ts index 489dd88..080020b 100644 --- a/src/library/index.ts +++ b/src/library/index.ts @@ -179,6 +179,9 @@ export interface LibraryOptions { // directory at open. The content cache treats an original already stored // at its save path as present. downloadDirectory?: string; + // How long after one refresh ends the background timer starts the next. + // `Infinity` starts none: the library then refreshes only as it opens and + // when `fresh()` or `backup()` asks. refreshIntervalSeconds?: number; onProgress?: RefreshProgressCallback; // The bounded request pools (issue #45), shared by the ML-data fetch (the @@ -627,7 +630,8 @@ export class Library { } private scheduleNext(): void { - if (this.closed) return; + // `setTimeout` would turn an infinite delay into 1 ms. + if (this.closed || this.intervalMs === Infinity) return; this.timer = setTimeout(() => { void this.runRefresh().then(() => this.scheduleNext()); }, this.intervalMs);