Keep artwork on disk and serve every image from iPX (#111)

The page loaded artwork from each publisher's server, or through /api/art, fetched every time,
for http-only hosts. Nothing was kept, every visit asked every publisher, and artwork went when
a publisher's server did.

- The page draws every image a feed or item names from /api/art. The first time, iPX fetches
  it (only an address a feed or item names, only an image, up to 5 MB, within the feed timeout)
  and keeps it in art/ beside the database, under a hash of its address with its type beside
  it (src/art.rs). Later it comes from disk, which marks it as used.
- art_cache_mb, a server setting on the admin page, 500 by default, caps what is kept: the
  sweep before each scan drops the least recently shown until it fits. 0 keeps nothing, and
  artwork is fetched through iPX each time. Settings saved before it get the default.
- Served from iPX's own address, someone else's image must stay an image: nosniff, and a CSP
  with sandbox, so an SVG opened on its own runs no script as iPX.
- The fixture server sends .jpg as image/jpeg, which /api/art requires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-02 13:50:46 +00:00
parent 38ebc7b02b
commit a00516687a
11 changed files with 196 additions and 15 deletions

View File

@@ -46,7 +46,7 @@ function mediaSession(e,f){
if(!('mediaSession' in navigator)) return;
navigator.mediaSession.metadata=new MediaMetadata({
title:e.title||'', artist:f?(f.title||f.id):'', album:f?(f.title||''):'',
artwork:(e.image||(f&&f.image))?[{src:e.image||f.image,sizes:'512x512'}]:[],
artwork:(e.image||(f&&f.image))?[{src:artSrc(e.image||f.image),sizes:'512x512'}]:[],
});
const h={play:()=>audio.play(),pause:()=>audio.pause(),
seekbackward:()=>audio.currentTime-=15,seekforward:()=>audio.currentTime+=30};