@@ -2,39 +2,86 @@ import fs from "fs";
22import Sharp from "sharp" ;
33import * as crypto from "crypto" ;
44
5- export async function getCachedImage ( image_url : string ) : Promise < string > {
6- const directory = "./src/assets/cache" ;
7- const name = crypto . createHash ( "md5" ) . update ( image_url ) . digest ( "hex" ) ;
8- const filename = `${ name } .webp` ;
9- const path = `${ directory } /${ filename } ` ;
10- console . log ( `Cache ${ path } ` ) ;
5+ const MAX_RETRIES = 3 ;
6+ const TIMEOUT_MS = 5000 ;
7+
8+ async function fetchWithTimeout (
9+ url : string ,
10+ timeout : number ,
11+ retryCount = 0
12+ ) : Promise < Response > {
13+ const controller = new AbortController ( ) ;
14+ const timeoutId = setTimeout ( ( ) => controller . abort ( ) , timeout ) ;
1115
1216 try {
13- if ( ! fs . existsSync ( path ) ) {
14- if ( ! fs . existsSync ( directory ) ) {
15- fs . mkdirSync ( directory , { recursive : true } ) ;
16- }
17+ const response = await fetch ( url , {
18+ signal : controller . signal ,
19+ headers : {
20+ "User-Agent" : "Mozilla/5.0 (compatible; AstroImageFetcher/1.0)" ,
21+ Accept : "image/*" ,
22+ } ,
23+ } ) ;
24+ clearTimeout ( timeoutId ) ;
25+
26+ if ( ! response . ok ) {
27+ throw new Error ( `HTTP ${ response . status } - ${ response . statusText } ` ) ;
28+ }
29+
30+ return response ;
31+ } catch ( error ) {
32+ clearTimeout ( timeoutId ) ;
33+
34+ if ( retryCount < MAX_RETRIES ) {
35+ console . warn (
36+ `Retrying fetch for ${ url } (attempt ${ retryCount + 1 } /${ MAX_RETRIES } )...`
37+ ) ;
38+ const backoffDelay = Math . pow ( 2 , retryCount ) * 1000 ;
39+ await new Promise ( ( resolve ) => setTimeout ( resolve , backoffDelay ) ) ;
40+ return fetchWithTimeout ( url , timeout , retryCount + 1 ) ;
41+ }
42+
43+ console . error ( `Failed to fetch ${ url } :` , error ) ;
44+ throw error ;
45+ }
46+ }
47+
48+ export async function getCachedImage ( imageUrl : string ) : Promise < string > {
49+ // Skip SVG files
50+ if ( imageUrl . trim ( ) . toLowerCase ( ) . endsWith ( ".svg" ) ) {
51+ console . log ( `Skipping SVG: ${ imageUrl } ` ) ;
52+ return imageUrl ;
53+ }
1754
18- const response = await fetch ( image_url ) ;
19- if ( ! response . ok ) {
20- throw new Error (
21- `Failed to fetch image: ${ response . status } ${ response . statusText } `
22- ) ;
55+ if ( imageUrl === "" ) {
56+ console . log ( `Skipping empty URL: ${ imageUrl } ` ) ;
57+ return imageUrl ;
58+ }
59+
60+ const cacheDirectory = "./public/cache" ;
61+ const imageHash = crypto . createHash ( "md5" ) . update ( imageUrl ) . digest ( "hex" ) ;
62+ const imageFilename = `${ imageHash } .jpg` ;
63+ const cachedImagePath = `${ cacheDirectory } /${ imageFilename } ` ;
64+ const publicImagePath = `${ "/cache" } /${ imageFilename } ` ;
65+
66+ try {
67+ if ( ! fs . existsSync ( cachedImagePath ) ) {
68+ if ( ! fs . existsSync ( cacheDirectory ) ) {
69+ fs . mkdirSync ( cacheDirectory , { recursive : true } ) ;
2370 }
2471
72+ const response = await fetchWithTimeout ( imageUrl , TIMEOUT_MS ) ;
2573 const arrayBuffer = await response . arrayBuffer ( ) ;
26- const buffer = Buffer . from ( arrayBuffer ) ;
74+ const imageBuffer = Buffer . from ( arrayBuffer ) ;
2775
2876 try {
29- await Sharp ( buffer ) . toFile ( path ) ;
77+ await Sharp ( imageBuffer ) . toFile ( cachedImagePath ) ;
3078 } catch ( err ) {
3179 console . error ( "Error converting image with Sharp:" , err ) ;
3280 throw new Error ( "Image conversion failed" ) ;
3381 }
3482 }
3583
36- const url = path . startsWith ( "./" ) ? path . slice ( 1 ) : path ;
37- return url ;
84+ return publicImagePath ;
3885 } catch ( err ) {
3986 console . error ( "Error in getCachedImage:" , err ) ;
4087 throw err ;
0 commit comments