data endpoint for entity 90008 (aka. a website)
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

update cover art caching to optimize the cover art size and also cache yt images

dawn eaae230b b2624d81

+32 -20
+27 -15
eunomia/src/lib/lastfm.ts
··· 1 1 import { env } from '$env/dynamic/private'; 2 + import sharp from 'sharp'; 2 3 import { get, writable } from 'svelte/store'; 3 4 4 5 const DID = 'did:plc:dfl62fgb7wtjj3fcbb72naae'; ··· 26 27 } 27 28 }; 28 29 29 - // Fetch and cache MusicBrainz cover art 30 - const fetchAndCacheCoverArt = async (releaseMbId: string): Promise<string | null> => { 31 - const cacheFile = `${COVER_ART_CACHE_DIR}/${releaseMbId}.jpg`; 30 + // Helper to fetch, optimize, and cache an image 31 + const fetchAndCacheImage = async (url: string, id: string): Promise<string | null> => { 32 + const cacheFile = `${COVER_ART_CACHE_DIR}/${id}.webp`; 32 33 33 34 // Check if already cached 34 35 try { 35 36 await Deno.stat(cacheFile); 36 - return `/cover_art/${releaseMbId}.jpg`; 37 + return `/cover_art/${id}.webp`; 37 38 } catch { 38 39 // Not cached, try to fetch 39 40 } 40 41 41 42 try { 42 - const mbUrl = `https://coverartarchive.org/release/${releaseMbId}/front-250`; 43 - const response = await fetch(mbUrl); 43 + const response = await fetch(url); 44 44 45 45 if (!response.ok) { 46 46 return null; 47 47 } 48 48 49 49 const imageData = await response.arrayBuffer(); 50 - await Deno.writeFile(cacheFile, new Uint8Array(imageData)); 51 50 52 - return `/cover_art/${releaseMbId}.jpg`; 51 + const sharpImg = sharp(imageData).resize({ width: 92 }).webp({ quality: 80 }); 52 + const optimizedImage = await sharpImg.toBuffer(); 53 + sharpImg.destroy(); 54 + 55 + await Deno.writeFile(cacheFile, new Uint8Array(optimizedImage)); 56 + 57 + return `/cover_art/${id}.webp`; 53 58 } catch (err) { 54 - console.log(`Failed to fetch MusicBrainz cover art for ${releaseMbId}:`, err); 59 + console.log(`Failed to fetch/cache image for ${id}:`, err); 55 60 return null; 56 61 } 57 62 }; 58 63 59 - // Get YouTube thumbnail URL 60 - const getYouTubeThumbnail = (originUrl: string | null | undefined): string | null => { 64 + // Fetch and cache MusicBrainz cover art 65 + const fetchAndCacheCoverArt = async (releaseMbId: string): Promise<string | null> => { 66 + const mbUrl = `https://coverartarchive.org/release/${releaseMbId}/front-250`; 67 + return fetchAndCacheImage(mbUrl, releaseMbId); 68 + }; 69 + 70 + // Fetch and cache YouTube thumbnail 71 + const fetchAndCacheYouTubeThumbnail = async (originUrl: string | null | undefined): Promise<string | null> => { 61 72 if (!originUrl) return null; 62 73 63 74 try { ··· 68 79 videoId = originUrl.split('youtu.be/')[1]?.split('?')[0]; 69 80 } 70 81 if (videoId) { 71 - return `https://img.youtube.com/vi/${videoId}/mqdefault.jpg`; 82 + const ytUrl = `https://img.youtube.com/vi/${videoId}/mqdefault.jpg`; 83 + return fetchAndCacheImage(ytUrl, videoId); 72 84 } 73 - } catch {} 85 + } catch { } 74 86 75 87 return null; 76 88 }; ··· 86 98 if (mbImage) return mbImage; 87 99 } 88 100 89 - // Fall back to YouTube thumbnail 90 - return getYouTubeThumbnail(originUrl); 101 + // Fall back to YouTube thumbnail (with caching) 102 + return fetchAndCacheYouTubeThumbnail(originUrl); 91 103 }; 92 104 93 105 export const getLastTrack = async () => {
+2 -2
eunomia/src/routes/(site)/+page.svelte
··· 178 178 ? 'object-cover' 179 179 : 'p-2'}" 180 180 style="border-style: none double none none; {data.lastTrack.image 181 - ? '' 182 - : 'image-rendering: pixelated;'}" 181 + ? 'image-rendering: smooth !important;' 182 + : 'image-rendering: pixelated !important;'}" 183 183 src={data.lastTrack.image ?? '/icons/cd_audio.webp'} 184 184 title={data.lastTrack.album} 185 185 />
+3 -3
eunomia/src/routes/cover_art/[mbid]/+server.ts
··· 2 2 import { error } from '@sveltejs/kit'; 3 3 4 4 export const GET = async ({ params }) => { 5 - const mbid = params.mbid?.replace('.jpg', ''); 5 + const mbid = params.mbid?.replace('.webp', '')?.replace('.jpg', ''); 6 6 7 7 if (!mbid) { 8 8 throw error(404, 'Missing MBID'); 9 9 } 10 10 11 11 const cacheDir = `${env.WEBSITE_DATA_DIR}/cover_art_cache`; 12 - const filePath = `${cacheDir}/${mbid}.jpg`; 12 + const filePath = `${cacheDir}/${mbid}.webp`; 13 13 14 14 try { 15 15 const file = await Deno.readFile(filePath); 16 16 return new Response(file, { 17 17 headers: { 18 - 'Content-Type': 'image/jpeg', 18 + 'Content-Type': 'image/webp', 19 19 'Cache-Control': 'public, max-age=31536000, immutable' 20 20 } 21 21 });