Thumbnail the whole library on request, and send the shards up
The grid fetches a preview only for cells that are actually browsed, which is the right posture over a link that must not be saturated to show one screen (FR-NC-3). The cost is that the thumbnail store ends up holding the fraction of the library someone happened to scroll past — and that store is the one derived thing worth syncing, since a second device that downloads the shards gets a full grid without touching a single RAW. So the complete set is worth an hour of range fetches paid once, deliberately, on a machine that can afford it. That is what this adds: a pass over every visible image with an `oc:fileid`, launched from the settings page and reported in the activity register like every other background job. The work list is what the store lacks rather than a flag in the catalog, so it is resumable by construction and safe to press twice. Lane-parallel like the metadata sweep, but it does what that sweep declined to. The store is `&mut` and cannot cross lanes, which is why dating the library skips thumbnails entirely; here the lanes fetch, decode and *encode*, and only the ~20 KB result crosses back to the one thread that owns the store and writes the chunk. The parallelism is real and the single-writer rule is not bent. Grid class only. The large class is ~860 MB of shards against ~200 MB on the reference library, paid by every device that syncs them; a photograph looked at closely still gets its large thumbnail from the interactive path. Dates come free — the header a preview needs is the header EXIF lives in — and the pass ends by pushing the shards to the server. A filled store that never leaves this device would be most of the cost for none of the point. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -69,6 +69,14 @@ export component SettingsPage inherits Rectangle {
|
||||
/// they are read.
|
||||
in property <int> activity-kept: 0;
|
||||
callback clear-finished();
|
||||
/// TRACES: FR-CAT-3 | FR-NC-3
|
||||
/// Whether the whole-library thumbnail pass is running, and whether there
|
||||
/// is a library for it to run over. Without the second, the button would
|
||||
/// be offered on a settings page reached before signing in, where pressing
|
||||
/// it could do nothing at all.
|
||||
in property <bool> thumbnailing: false;
|
||||
in property <bool> library-open: false;
|
||||
callback thumbnail-library();
|
||||
|
||||
// --- cache ---------------------------------------------------------
|
||||
in-out property <string> original-budget;
|
||||
@@ -305,6 +313,38 @@ export component SettingsPage inherits Rectangle {
|
||||
job: row;
|
||||
}
|
||||
|
||||
// TRACES: FR-CAT-3 | FR-NC-3 | FR-NC-7
|
||||
// The one job here that is started rather than
|
||||
// observed, and it belongs on this page rather than
|
||||
// in the header: it runs for an hour, it is asked for
|
||||
// once, and its progress appears in the list directly
|
||||
// above.
|
||||
if root.library-open: Rectangle {
|
||||
height: Theme.gap-sm;
|
||||
}
|
||||
|
||||
if root.library-open: Caption {
|
||||
text: "Thumbnails are built for photographs as you "
|
||||
+ "browse them. This builds the rest in one "
|
||||
+ "pass and sends them to the server, so "
|
||||
+ "your other devices show a full library "
|
||||
+ "without downloading anything themselves.";
|
||||
wrap: word-wrap;
|
||||
}
|
||||
|
||||
if root.library-open: Rectangle {
|
||||
height: Theme.control-height;
|
||||
|
||||
Button {
|
||||
x: 0;
|
||||
text: root.thumbnailing
|
||||
? "Thumbnailing…"
|
||||
: "Thumbnail the whole library";
|
||||
enabled: !root.thumbnailing;
|
||||
clicked => { root.thumbnail-library(); }
|
||||
}
|
||||
}
|
||||
|
||||
if root.activity-kept > 0: Rectangle {
|
||||
height: Theme.control-height;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user