quak files takes the collection ID as an optional argument (closes #211)
check / check (push) Canceled after 0s

Mandatory arguments are positional, so `quak files` takes the collection
ID as an optional argument and the `--collection` option is removed.
With a collection ID it lists that collection's files. Without one it
lists every file in the account, collection by collection, each file
once: the library keeps a record of a file for each collection that
holds it, and only the first collection's record is listed, so that
collection is its `collectionID` in `--json`. The `--help` text, the
README and `TODO.md` say so.

Model: opus-5-5
This commit is contained in:
2026-10-18 18:07:57 +00:00
parent 66e3ac77e8
commit c34e05cfe0
9 changed files with 182 additions and 69 deletions
+9 -3
View File
@@ -269,15 +269,21 @@ export const collectionsCommand = async (
}
};
// Lists the files of the collection `collectionIDStr` names, or without it every
// file in the account, each once (issue #211).
export const filesCommand = async (
ctx: CliContext,
opts: { collection: string; json?: boolean },
collectionIDStr: string | undefined,
opts: { json?: boolean },
): Promise<number> => {
await init();
const client = requireSession(ctx);
if (!client) return 3;
const collectionID = parseID(ctx, "collection", opts.collection);
if (collectionID === undefined) return 1;
const collectionID =
collectionIDStr === undefined
? undefined
: parseID(ctx, "collection", collectionIDStr);
if (collectionIDStr !== undefined && collectionID === undefined) return 1;
const lib = await openReadLibrary(ctx, client, opts);
try {
+25 -6
View File
@@ -1,6 +1,6 @@
// How the CLI's read commands obtain current data.
//
// `collections`, `files --collection`, `get`, and `get-thumb` must answer for
// `collections`, `files`, `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
@@ -14,7 +14,10 @@
// 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`.
// metadata via `cli-output.ts`. `files` without a collection ID lists every
// file in the account once (issue #211): the library keeps a record of a file
// for each collection that holds it, and only the first collection's record is
// listed.
import type { Collection, EnteFile } from "./model/types.js";
import type { Photo, PhotosAPI } from "./library/index.js";
@@ -27,6 +30,7 @@ export interface FreshReadLibrary {
fresh(): Promise<unknown>;
freshCollections(): Promise<Collection[]>;
getCollection(id: number): Collection | undefined;
listCollections(): Collection[];
listFiles(collectionID: number): EnteFile[];
getFileByID(fileID: number): EnteFile | undefined;
photos: Pick<PhotosAPI, "byID">;
@@ -40,14 +44,29 @@ export const 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".
// the caller can tell "no such collection" from "an empty collection". Without
// a collection ID, every file in the account, each once, as held by the first
// collection that holds it.
export const freshFiles = async (
lib: FreshReadLibrary,
collectionID: number,
collectionID?: number,
): Promise<EnteFile[] | undefined> => {
await lib.fresh();
if (!lib.getCollection(collectionID)) return undefined;
return lib.listFiles(collectionID);
if (collectionID !== undefined) {
return lib.getCollection(collectionID)
? lib.listFiles(collectionID)
: undefined;
}
const listed = new Set<number>();
const files: EnteFile[] = [];
for (const collection of lib.listCollections()) {
for (const file of lib.listFiles(collection.id)) {
if (listed.has(file.id)) continue;
listed.add(file.id);
files.push(file);
}
}
return files;
};
// One file, current as of a forced refresh, resolved to both its content