- ffmpeg writes JPEG XL with -update 1, so a % in a file name is never an
image-sequence pattern, and reads videos as file:<path>. The clear and a
startup sweep remove temp files that interrupted writes left behind.
- Convert outdated converts each group of same-stem files once, from the
first source in the R2 order. An old preview that is already gone counts
as removed, and each file's bytes count once. A current preview is only
kept if it decodes.
- An encoder that fails makes the write detect encoders again once. Another
encoder is used if one is found; with none, the file is written as WebP
and the availability Appearance shows is refreshed.
- Changing the thumbnail format, quality or size while Convert outdated runs
returns 409 busy. If the format still changes underneath, the job stops
without removing anything.
- Legacy preview spellings are matched case-insensitively, so X_thumb.JPG
counts as outdated.
- History's cover uses the same lookup as Images. It serves .jxl only when
Accept lists image/jxl, and otherwise the original or the placeholder.
- A video whose thumbnail the browser cannot show keeps its own element
(Images) or its label (Library).
Automatic previews (capture and first view), the thumbnail job, the
legacy gallery routes and the history route write <stem>_thumb.webp
(or .jxl/.jpg) with the configured quality and size limit, for images
and videos alike: video frames, ffmpeg's included (a PNG on stdout,
never upscaled), and the placeholder are encoded like images.
Listings, duplicates, enrichment, the autotag scan and the history
route show any preview the file owns, the current format first, so
older _thumb.png previews keep working until converted. A request for
an older name answers the current preview. Previews are served with
an explicit image/webp or image/jxl type and the same cache headers.
The plan and status count missing, outdated (another format) and
ready previews; the missing job never writes beside an outdated one.
Removing a file removes its previews in every format; the clear
removes .webp and .jxl too. Encoder processes are killed at shutdown.
A preview is only a placeholder: inspection refuses paths under
thumbnails/ and always reads the original, and the inspector cover
accepts the listing's preview in any format the file owns.
* fix(thumbnails): generate thumbnails automatically and use them everywhere
New outputs never got a preview, so the workspace grid loaded full-size
originals (844 of 19,907 images on a live install, exactly the newest).
- Listings always give images and videos a thumbnail URL; a missing
preview is made by the serve route on first view (bounded, single
flight per file) and falls back to the original, remembering files
that cannot be decoded.
- The image monitor queues each settled output's preview on one
background worker that follows Worker threads.
- A scan that leaves 200 or more previews missing starts the visible
thumbnail job; fewer go through the worker.
- Automatic previews wait for a restore, a duplicate removal, a sources
change, the gallery's clear and exclusive jobs.
- The inspector cover uses the preview; the Overview row counts
previews that failed.
* fix(thumbnails): keep the video fallback tested and stop the preview worker before cleanup
A video listed without a preview still renders its own element and is
released on refresh. Test teardowns and the browser reset stop the
automatic preview worker before the temp tree goes, so no preview lands
after cleanup (Windows). TESTING.md documents automatic previews.
* fix(thumbnails): admit previews on the loop and never send a video to an img
Final review fixes for automatic previews:
- A video preview that cannot be made is the placeholder frame (no-store),
never the video; OpenCV failures fall through to ffmpeg and the
placeholder. Cards and the inspector cover swap back to the video
element when the preview image fails.
- View requests are admitted on the event loop (3 writers, one flight per
file, at most 64 waiting; past that the fallback at once). Writes run on
a small dedicated pool, never the default executor. The history route
shares the admission, flight key and link checks.
- Drive, UNC, empty and dot segments are refused as text; the folder
listing runs inside the admission.
- Holds are checked after the slot and again before the rename; only
decode failures are remembered.
- Bounded, logged shutdown: ffmpeg is killed, no worker restarts after
cleanup, and a reload stops the old worker.
- The listing says whether the preview file exists (thumbnail_ready) for
the legacy gallery's dot; media in a nested thumbnails folder keeps its
original; file names the server quotes match in the inspector.
* fix(thumbnails): cover the image fallback card and document the admission
An image listed without a preview (a nested thumbnails folder) still
shows its original; TESTING.md describes the view admission, the video
placeholder, the holds before the rename and the bounded shutdown.
* fix(thumbnails): let an idle preview worker leave and fit case-insensitive filesystems
The capture worker exits once its queue is empty, so no thread outlives
its work; the next output starts a fresh one. The mixed-case source test
expects no folder listing where the exact name already resolves (macOS,
Windows).
* fix(thumbnails): answer shared flights with the outcome only and restart a failed worker
The history and serve routes share a flight key, so a flight now answers
only the ensure outcome whichever route started it; the serve route
derives the preview path and, for a fallback, looks its source up again
on the view pool. A view joining a history flight no longer gets a 500
or an image answered with the video placeholder.
The capture worker clears its running state however its loop ends, and a
worker that cannot start leaves the file queued for the next enqueue.