feat: low-res webp thumbnails for library previews

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
This commit is contained in:
2026-07-23 19:19:39 +00:00
parent 044eb7f1ae
commit b705d77240
2 changed files with 15 additions and 7 deletions
+1 -1
View File
@@ -66,7 +66,7 @@ export default function LibraryApp() {
{items.map((v) => (
<article key={v.id} className={`kachel ${sel.has(v.id) ? 'gewaehlt' : ''}`}>
<div className="kachel-bild" onClick={() => toggle(v.id)}>
<img src={`/api/items/${v.id}/file`} alt="" loading="lazy" />
<img src={`/api/items/${v.id}/file?thumb=1`} alt="" loading="lazy" />
<span className="haken">{sel.has(v.id) ? '✓' : ''}</span>
</div>
<input className="name" defaultValue={v.filename || ''} onBlur={(e) => rename(v.id, e.target.value)} />
+14 -6
View File
@@ -7,15 +7,23 @@ export const prerender = false;
// Streamt das Ergebnis- (oder Quell-)Bild aus dem Objektspeicher.
export const GET: APIRoute = async ({ params, url, locals }) => {
if (!locals.user) return new Response('Unauthorized', { status: 401 });
const which = url.searchParams.get('src') === '1' ? 'source_path' : 'result_path';
const item = await one<any>(`SELECT ${which} AS path, filename, has_alpha FROM items WHERE id=$1`, [params.id]);
if (!item?.path) return new Response('Nicht gefunden', { status: 404 });
const q = url.searchParams;
const wantThumb = q.get('thumb') === '1';
const wantSrc = q.get('src') === '1';
const item = await one<any>(
`SELECT source_path, result_path, thumb_path, filename, has_alpha FROM items WHERE id=$1`, [params.id]);
// Vorschau: Thumbnail bevorzugen, sonst Ergebnis. Quelle nur explizit.
const path = wantSrc ? item?.source_path
: wantThumb ? (item?.thumb_path || item?.result_path)
: item?.result_path;
if (!path) return new Response('Nicht gefunden', { status: 404 });
try {
const buf = await getObject(item.path);
const download = url.searchParams.get('download') === '1';
const buf = await getObject(path);
const download = q.get('download') === '1';
const isWebp = path.endsWith('.webp');
return new Response(buf, {
headers: {
'Content-Type': item.has_alpha ? 'image/png' : 'image/png',
'Content-Type': isWebp ? 'image/webp' : 'image/png',
'Cache-Control': 'private, max-age=300',
...(download ? { 'Content-Disposition': `attachment; filename="${item.filename || 'klarbild.png'}"` } : {}),
},