check / check (push) Canceled after 0s
On an empty cache the read commands opened their library with a refresh that fetched and decrypted the files of every collection in turn, printing nothing and storing nothing until it had them all, so `quak collections` seemed to hang on a large account. The new `lib.freshCollections()` is the fresh read of the collection list alone: one request, and nothing stored, so a later refresh still fetches every collection's files. The read commands open their library with the new `refreshOnOpen: false` and make only the refresh they ask for. A refresh sends a "progress" event after each collection whose files it fetched; the commands that wait on one print it on stderr, except with `--json`. Model: opus-5-5
66 lines
2.9 KiB
TypeScript
66 lines
2.9 KiB
TypeScript
// How the CLI's read commands obtain current data.
|
|
//
|
|
// `collections`, `files --collection`, `get`, and `get-thumb` must answer for
|
|
// the account's state at the moment the command runs, not for whatever the
|
|
// local cache last happened to hold (owner amendment, issue #36). `files`,
|
|
// `get` and `get-thumb` therefore force a server round-trip through
|
|
// `Library.fresh()` and only then read — so a collection, file, or metadata
|
|
// change made elsewhere is visible. `collections` asks the library for the
|
|
// collection list alone through `Library.freshCollections()` (issue #210): a
|
|
// refresh would also fetch the files of every collection, which on a large
|
|
// account takes long and which `collections` does not print.
|
|
//
|
|
// `collections` lists in the server's order and `files` in the library's own
|
|
// enumeration order (`listFiles()`), the orders the pre-library CLI printed,
|
|
// rather than the `albums`/`photos` projection's newest-first order, which
|
|
// re-sorts the rows. The field values still come from each record's raw
|
|
// metadata via `cli-output.ts`.
|
|
|
|
import type { Collection, EnteFile } from "./model/types.js";
|
|
import type { Photo, PhotosAPI } from "./library/index.js";
|
|
|
|
// The slice of `Library` these helpers read. `Library` satisfies it
|
|
// structurally; a test can drive them with a stand-in that records the
|
|
// `fresh()` and `freshCollections()` calls and serves records in a known
|
|
// enumeration order.
|
|
export interface FreshReadLibrary {
|
|
fresh(): Promise<unknown>;
|
|
freshCollections(): Promise<Collection[]>;
|
|
getCollection(id: number): Collection | undefined;
|
|
listFiles(collectionID: number): EnteFile[];
|
|
getFileByID(fileID: number): EnteFile | undefined;
|
|
photos: Pick<PhotosAPI, "byID">;
|
|
}
|
|
|
|
// Every live collection, current as of one collections request, in the
|
|
// server's order.
|
|
export const freshCollections = (
|
|
lib: FreshReadLibrary,
|
|
): Promise<Collection[]> => lib.freshCollections();
|
|
|
|
// The files of one collection, current as of a forced refresh, in enumeration
|
|
// order. `undefined` (not an empty list) when the collection does not exist, so
|
|
// the caller can tell "no such collection" from "an empty collection".
|
|
export const freshFiles = async (
|
|
lib: FreshReadLibrary,
|
|
collectionID: number,
|
|
): Promise<EnteFile[] | undefined> => {
|
|
await lib.fresh();
|
|
if (!lib.getCollection(collectionID)) return undefined;
|
|
return lib.listFiles(collectionID);
|
|
};
|
|
|
|
// One file, current as of a forced refresh, resolved to both its content
|
|
// handle (`Photo`, for fetching bytes) and its raw record (`EnteFile`, for the
|
|
// default output name and field values). `undefined` when the file is unknown.
|
|
export const freshFile = async (
|
|
lib: FreshReadLibrary,
|
|
fileID: number,
|
|
): Promise<{ photo: Photo; file: EnteFile } | undefined> => {
|
|
await lib.fresh();
|
|
const photo = lib.photos.byID({ fileID });
|
|
const file = lib.getFileByID(fileID);
|
|
if (!photo || !file) return undefined;
|
|
return { photo, file };
|
|
};
|