quak helper fix-missing-thumbnails --file on an empty cache stops fetching collections once it finds every file (closes #223)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
`helper fix-missing-thumbnails --file` used to wait for a refresh, which on an empty cache fetched the files of every collection. It now finds the named files through the new `freshFilesByID` in `src/cli-read.ts`, by the rule `freshFile` follows. On an empty cache it reads the collection list, then one collection's files at a time, and stores none of the collections or file records it reads. It searches the most recently updated collection first. So a file in several albums is still named after the first of them that `Library.albums.list()` lists. `fixMissingThumbnails` now takes the records the command found, and fetches each original through `lib.originalOf()`. Model: opus-5-5
This commit was merged in pull request #227.
This commit is contained in:
1 parent
f65c237e2c
commit
a1ea47f9a8
9 files changed
+322
-70
No files matched your search
+11
-36
@@ -5,7 +5,7 @@ import type { Client } from "./client.js";
|
||||
import type { Library } from "./library/index.js";
|
||||
import { ApiError } from "./api/client.js";
|
||||
import { encryptBlob, toBase64 } from "./crypto/index.js";
|
||||
import type { EnteFile } from "./model/types.js";
|
||||
import type { Collection, EnteFile } from "./model/types.js";
|
||||
|
||||
// The server refuses a thumbnail larger than the one it already records for the
|
||||
// file (`thumbnail.size`, the encrypted size), so these encodings are tried
|
||||
@@ -226,16 +226,18 @@ const encryptThumbnailWithin = (
|
||||
return undefined;
|
||||
};
|
||||
|
||||
// Regenerate and upload a thumbnail for each requested file. Originals are read
|
||||
// through the library's content cache (`photo.original()`); the generated
|
||||
// thumbnail is JPEG-encoded, encrypted under the file's own key, and registered
|
||||
// with the server — the encrypt-and-upload path is unchanged. Each file is
|
||||
// Regenerate and upload a thumbnail for each requested file. `found` holds the
|
||||
// record of each requested file that a collection holds, and the collection it
|
||||
// was found in. Each original is read through the library's content cache, by
|
||||
// `lib.originalOf(file)`. The new thumbnail is a JPEG, encrypted under the
|
||||
// file's own key. It is uploaded, then registered with the server. Each file is
|
||||
// resolved to one outcome (fixed / skipped / failed) and a failure on one file
|
||||
// never stops the others.
|
||||
export const fixMissingThumbnails = async (
|
||||
lib: Library,
|
||||
client: Client,
|
||||
fileIDs: number[],
|
||||
found: Map<number, { file: EnteFile; collection: Collection }>,
|
||||
onProgress?: ProgressCallback,
|
||||
): Promise<ThumbnailFixResult[]> => {
|
||||
const log = onProgress ?? (() => {});
|
||||
@@ -243,31 +245,8 @@ export const fixMissingThumbnails = async (
|
||||
const api = client.getApiClient();
|
||||
const { userID } = client.whoami();
|
||||
|
||||
// Resolve each requested fileID to its file record and owning album by
|
||||
// enumerating the library, each file taken from the first album that holds
|
||||
// it. The raw `EnteFile` carries the per-file key the thumbnail is
|
||||
// encrypted under, which the projected records deliberately do not.
|
||||
const wanted = new Set(fileIDs);
|
||||
const fileMap = new Map<
|
||||
number,
|
||||
{ file: EnteFile; collectionName: string }
|
||||
>();
|
||||
for (const album of lib.albums.list()) {
|
||||
for (const photo of album.photos.list()) {
|
||||
if (!wanted.has(photo.fileID) || fileMap.has(photo.fileID))
|
||||
continue;
|
||||
const file = lib.getFile(album.collectionID, photo.fileID);
|
||||
if (file) {
|
||||
fileMap.set(photo.fileID, {
|
||||
file,
|
||||
collectionName: album.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const fileID of fileIDs) {
|
||||
const entry = fileMap.get(fileID);
|
||||
const entry = found.get(fileID);
|
||||
if (!entry) {
|
||||
results.push({
|
||||
fileID,
|
||||
@@ -279,7 +258,8 @@ export const fixMissingThumbnails = async (
|
||||
continue;
|
||||
}
|
||||
|
||||
const { file, collectionName } = entry;
|
||||
const { file } = entry;
|
||||
const collectionName = entry.collection.name;
|
||||
const title = file.metadata.title;
|
||||
|
||||
const skipReason = reasonToSkip(file, userID);
|
||||
@@ -297,15 +277,10 @@ export const fixMissingThumbnails = async (
|
||||
const maxSize = file.thumbnail.size!;
|
||||
|
||||
try {
|
||||
const photo = lib.photos.byID({ fileID });
|
||||
if (!photo) {
|
||||
throw new Error("file not present in the library cache");
|
||||
}
|
||||
|
||||
log(
|
||||
`[${collectionName}] Downloading ${title} for thumbnail generation...`,
|
||||
);
|
||||
const { path } = await photo.original();
|
||||
const { path } = await lib.originalOf(file);
|
||||
const fileBytes = new Uint8Array(readFileSync(path));
|
||||
|
||||
if (!isJpeg(fileBytes)) {
|
||||
|
||||
Reference in new issue
Block a user