Changeset e67828e in Klonkt for src/services/ThumbnailService.js
- Timestamp:
- 07/01/2026 10:38:02 PM (2 months ago)
- Branches:
- main
- Children:
- 0877771
- Parents:
- 5e52448
- File:
-
- 1 edited
-
src/services/ThumbnailService.js (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/services/ThumbnailService.js
r5e52448 re67828e 185 185 if (fs.existsSync(cached)) return cached; 186 186 187 let buf ;187 let buf, isVideo = false; 188 188 try { 189 189 const r = await safeFetch(url); 190 190 if (!r.ok) return null; 191 if (!(r.headers.get('content-type') || '').startsWith('image/')) return null; 192 if (parseInt(r.headers.get('content-length') || '0', 10) > 12 * 1024 * 1024) return null; 193 buf = Buffer.from(await r.arrayBuffer()); 191 const ct = r.headers.get('content-type') || ''; 192 isVideo = ct.startsWith('video/'); 193 if (!ct.startsWith('image/') && !isVideo) return null; 194 if (isVideo) { 195 // Remote video → poster frame (feed/tile posters). Don't buffer the whole file: re-fetch 196 // a bounded head (first 4MB) — enough for ffmpeg to decode the first frame of a faststart 197 // mp4 (the web-streaming norm). A moov-at-end file just fails → null → the route's 302 198 // fallback, same as before this path existed. 199 try { if (r.body && r.body.cancel) r.body.cancel(); } catch { /* ignore */ } 200 const rv = await safeFetch(url, { headers: { Range: 'bytes=0-4194303' } }); 201 if (!rv.ok && rv.status !== 206) return null; 202 buf = Buffer.from(await rv.arrayBuffer()); 203 if (!buf.length) return null; 204 } else { 205 if (parseInt(r.headers.get('content-length') || '0', 10) > 12 * 1024 * 1024) return null; 206 buf = Buffer.from(await r.arrayBuffer()); 207 } 194 208 } catch (e) { 195 209 console.warn('[thumb-remote] fetch failed for', url, '-', e.message); … … 199 213 // ffmpeg-static can't decode an animated WebP (the doomed downscale just logs an error), and a 200 214 // flattened GIF/animated WebP loses its motion → skip it and let the route serve the ORIGINAL 201 // (keeps the animation; mirrors the local path's isAnimatedSrc guard). 202 if (isAnimatedBuf(buf)) return null; 215 // (keeps the animation; mirrors the local path's isAnimatedSrc guard). Video heads skip this 216 // (they're not webp/gif) and go straight to the single-frame extract. 217 if (!isVideo && isAnimatedBuf(buf)) return null; 203 218 204 219 await fs.promises.mkdir(path.dirname(cached), { recursive: true });
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)