quak get and get-thumb on an empty cache stop fetching collections once they find the file (closes #222)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
`quak get` and `quak get-thumb` used to wait for a refresh, which on an empty cache fetched the files of every collection. Now, on an empty cache, `freshFile` reads the collection list, then one collection's files at a time, and stops at the first that holds the file. That search stores nothing. Once the cache holds the account's collections, `freshFile` refreshes as before and looks the file up in the cache. The commands fetch the content through the new `Library.originalOf()` and `Library.thumbnailOf()`, which take the file record itself, since the store may not hold it. On an empty cache they no longer print a refresh's progress lines. The `-q` test that relied on `get` refreshing now runs `helper fix-missing-thumbnails`. Model: opus-5-5
This commit was merged in pull request #226.
This commit is contained in:
1 parent
af1491966c
commit
f65c237e2c
11 files changed
+278
-109
No files matched your search
+38
-11
@@ -581,14 +581,34 @@ export class ContentCache implements PhotoContent, ThumbnailsAPI {
|
||||
fileID: number,
|
||||
opts?: ContentOptions,
|
||||
): Promise<ContentResult> {
|
||||
return this.get(fileID, "original", "on-demand", opts?.onProgress);
|
||||
const file = this.fileOrThrow(fileID);
|
||||
return this.get(file, "original", "on-demand", opts?.onProgress);
|
||||
}
|
||||
|
||||
async thumbnail(
|
||||
fileID: number,
|
||||
opts?: ContentOptions,
|
||||
): Promise<ContentResult> {
|
||||
return this.get(fileID, "thumbnail", "on-demand", opts?.onProgress);
|
||||
const file = this.fileOrThrow(fileID);
|
||||
return this.get(file, "thumbnail", "on-demand", opts?.onProgress);
|
||||
}
|
||||
|
||||
// The original of `file`, fetched and cached as `original` does. `file` is
|
||||
// used as given, so `getFile` need not know it.
|
||||
async originalOf(
|
||||
file: EnteFile,
|
||||
opts?: ContentOptions,
|
||||
): Promise<ContentResult> {
|
||||
return this.get(file, "original", "on-demand", opts?.onProgress);
|
||||
}
|
||||
|
||||
// The thumbnail of `file`, fetched and cached as `thumbnail` does. `file`
|
||||
// is used as given, so `getFile` need not know it.
|
||||
async thumbnailOf(
|
||||
file: EnteFile,
|
||||
opts?: ContentOptions,
|
||||
): Promise<ContentResult> {
|
||||
return this.get(file, "thumbnail", "on-demand", opts?.onProgress);
|
||||
}
|
||||
|
||||
// Put the original at the save path of `file` under the download directory
|
||||
@@ -615,7 +635,7 @@ export class ContentCache implements PhotoContent, ThumbnailsAPI {
|
||||
destination: string,
|
||||
): Promise<ContentResult> {
|
||||
const result = await this.acquire(
|
||||
fileID,
|
||||
this.fileOrThrow(fileID),
|
||||
"original",
|
||||
"on-demand",
|
||||
undefined,
|
||||
@@ -638,8 +658,7 @@ export class ContentCache implements PhotoContent, ThumbnailsAPI {
|
||||
fileID: number,
|
||||
destination: string,
|
||||
): Promise<{ path: string; videoPath?: string }> {
|
||||
const file = this.getFile(fileID);
|
||||
if (!file) throw new Error(`content cache: unknown file ${fileID}`);
|
||||
const file = this.fileOrThrow(fileID);
|
||||
return this.pools.content.run(
|
||||
async () => {
|
||||
const fetched = await this.fetchInto(
|
||||
@@ -721,7 +740,8 @@ export class ContentCache implements PhotoContent, ThumbnailsAPI {
|
||||
onProgress: ((event: EnsureEvent) => void) | undefined,
|
||||
): Promise<EnsureResult> {
|
||||
try {
|
||||
const result = await this.acquire(fileID, kind, priority, signal);
|
||||
const file = this.fileOrThrow(fileID);
|
||||
const result = await this.acquire(file, kind, priority, signal);
|
||||
const status = result.cached ? "skipped" : "done";
|
||||
onProgress?.({ fileID, status, path: result.path });
|
||||
return { fileID, path: result.path };
|
||||
@@ -736,8 +756,16 @@ export class ContentCache implements PhotoContent, ThumbnailsAPI {
|
||||
}
|
||||
}
|
||||
|
||||
// The record `getFile` gives for `fileID`. It throws for a file `getFile`
|
||||
// does not know.
|
||||
private fileOrThrow(fileID: number): EnteFile {
|
||||
const file = this.getFile(fileID);
|
||||
if (!file) throw new Error(`content cache: unknown file ${fileID}`);
|
||||
return file;
|
||||
}
|
||||
|
||||
private async get(
|
||||
fileID: number,
|
||||
file: EnteFile,
|
||||
kind: Kind,
|
||||
priority: Priority,
|
||||
onProgress: ((event: ContentEvent) => void) | undefined,
|
||||
@@ -745,7 +773,7 @@ export class ContentCache implements PhotoContent, ThumbnailsAPI {
|
||||
const onByte: ProgressCallback | undefined = onProgress
|
||||
? (bytesDone) => onProgress({ status: "downloading", bytesDone })
|
||||
: undefined;
|
||||
const result = await this.acquire(fileID, kind, priority, undefined, {
|
||||
const result = await this.acquire(file, kind, priority, undefined, {
|
||||
onByte,
|
||||
});
|
||||
onProgress?.(
|
||||
@@ -767,7 +795,7 @@ export class ContentCache implements PhotoContent, ThumbnailsAPI {
|
||||
// photo's original is present only with its video, and is returned with
|
||||
// it.
|
||||
private async acquire(
|
||||
fileID: number,
|
||||
file: EnteFile,
|
||||
kind: Kind,
|
||||
priority: Priority,
|
||||
signal: AbortSignal | undefined,
|
||||
@@ -778,8 +806,7 @@ export class ContentCache implements PhotoContent, ThumbnailsAPI {
|
||||
videoPath?: string;
|
||||
cached: boolean;
|
||||
}> {
|
||||
const file = this.getFile(fileID);
|
||||
if (!file) throw new Error(`content cache: unknown file ${fileID}`);
|
||||
const fileID = file.id;
|
||||
const isLivePhoto =
|
||||
kind === "original" && file.metadata.fileType === "livePhoto";
|
||||
|
||||
|
||||
@@ -66,6 +66,8 @@ import {
|
||||
ContentCache,
|
||||
savePath,
|
||||
storedAtSavePath,
|
||||
type ContentOptions,
|
||||
type ContentResult,
|
||||
type ContentSource,
|
||||
type ThumbnailsAPI,
|
||||
type EnsureOptions,
|
||||
@@ -644,6 +646,36 @@ export class Library {
|
||||
return [...byID.values()];
|
||||
}
|
||||
|
||||
// The original of `file`, fetched and cached as `Photo.original()` does.
|
||||
// `file` is used as given, so the store need not hold it. On an empty
|
||||
// cache, `quak get` passes a file that `freshFiles()` returned.
|
||||
async originalOf(
|
||||
file: EnteFile,
|
||||
opts?: ContentOptions,
|
||||
): Promise<ContentResult> {
|
||||
if (!this.cache) {
|
||||
throw new Error(
|
||||
"originalOf requires a library opened with a content cache",
|
||||
);
|
||||
}
|
||||
return this.cache.originalOf(file, opts);
|
||||
}
|
||||
|
||||
// The thumbnail of `file`, fetched and cached as `Photo.thumbnail()` does.
|
||||
// `file` is used as given, so the store need not hold it. On an empty
|
||||
// cache, `quak get-thumb` passes a file that `freshFiles()` returned.
|
||||
async thumbnailOf(
|
||||
file: EnteFile,
|
||||
opts?: ContentOptions,
|
||||
): Promise<ContentResult> {
|
||||
if (!this.cache) {
|
||||
throw new Error(
|
||||
"thumbnailOf requires a library opened with a content cache",
|
||||
);
|
||||
}
|
||||
return this.cache.thumbnailOf(file, opts);
|
||||
}
|
||||
|
||||
// Back up every in-scope file to `opts.downloadDirectory`, or else the
|
||||
// library's, each original at its save path, with a durable failure
|
||||
// ledger (issue #51). Takes the lock in that directory first, unless the
|
||||
|
||||
Reference in new issue
Block a user