Root Cause
The original implementation fetched poster artwork for the
newly-selected item synchronously on the main render thread by shelling
out to curl:
load_poster_art → resolve_art_path(file, true) → cached_remote_art_path(url, true) → blocking curl with a 10-second ceiling
Love2D is single-threaded. The --max-time 10 ceiling
means the entire application freezes for up to 10 seconds per uncached
poster while curl downloads the image. During this
time:
- Render loop stalls (no frames drawn)
- Input events queue but are not processed
- User experience: pressing Down freezes the screen, then it jumps
This happens for every poster not already in
data/image-cache/. The cache mitigates repeat visits to the
same library position, but cold scrolls through a large library
(Jellyfin Movies) trigger the fetch for every new selection.
The follow-up RG smoke test found two remaining blockers after fetches moved to a worker:
- The Jellyfin catalog was still fetched synchronously from
love.load, so the app could show only local files after an empty response, then block for several seconds on a later startup while VOD titles merged. - Cached Jellyfin posters were still full-size primary JPEGs and were decoded synchronously on the Love2D main thread when selection changed or grid cells rendered.
The apparent black-screen incident was also traced to R2 lock mode
calling batocera-brightness dispoff; it was a visible-state
problem rather than a Love2D crash.