@@ -5,75 +5,70 @@ import sharp from 'sharp'
5
5
import { version } from '../../package.json'
6
6
import type { SponsorkitConfig , Sponsorship } from '../types'
7
7
8
+ async function fetchImage ( url : string ) {
9
+ const arrayBuffer = await $fetch ( url , {
10
+ responseType : 'arrayBuffer' ,
11
+ headers : {
12
+ 'User-Agent' : `Mozilla/5.0 Chrome/124.0.0.0 Safari/537.36 Sponsorkit/${ version } ` ,
13
+ } ,
14
+ } )
15
+ return Buffer . from ( arrayBuffer )
16
+ }
17
+
8
18
export async function resolveAvatars (
9
19
ships : Sponsorship [ ] ,
10
20
getFallbackAvatar : SponsorkitConfig [ 'fallbackAvatar' ] ,
11
21
t = consola ,
12
22
) {
13
- const fallbackAvatar = await ( async ( ) => {
23
+ const fallbackAvatar = await ( ( ) => {
14
24
if ( typeof getFallbackAvatar === 'string' ) {
15
- const data = await $fetch ( getFallbackAvatar , { responseType : 'arrayBuffer' } )
16
- return Buffer . from ( data )
25
+ return fetchImage ( getFallbackAvatar )
17
26
}
18
27
if ( getFallbackAvatar )
19
28
return getFallbackAvatar
20
- return undefined
21
29
} ) ( )
22
30
23
- const fallbackDataUri = fallbackAvatar && ( await round ( fallbackAvatar , 0.5 , 100 ) ) . toString ( 'base64' )
24
-
25
31
const pLimit = await import ( 'p-limit' ) . then ( r => r . default )
26
32
const limit = pLimit ( 15 )
27
33
28
34
return Promise . all ( ships . map ( ship => limit ( async ( ) => {
29
- const pngArrayBuffer = ( ship . privacyLevel === 'PRIVATE' || ! ship . sponsor . avatarUrl )
30
- ? fallbackAvatar
31
- : await $fetch ( ship . sponsor . avatarUrl , {
32
- responseType : 'arrayBuffer' ,
33
- headers : {
34
- 'User-Agent' : `Mozilla/5.0 Chrome/124.0.0.0 Safari/537.36 Sponsorkit/${ version } ` ,
35
- } ,
36
- } )
37
- . catch ( ( e ) => {
38
- t . error ( `Failed to fetch avatar for ${ ship . sponsor . login || ship . sponsor . name } [${ ship . sponsor . avatarUrl } ]` )
39
- t . error ( e )
40
- if ( fallbackAvatar )
41
- return fallbackAvatar
42
- throw e
43
- } )
35
+ if ( ship . privacyLevel === 'PRIVATE' || ! ship . sponsor . avatarUrl ) {
36
+ ship . sponsor . avatarBuffer = fallbackAvatar
37
+ return
38
+ }
44
39
45
- if ( ship . privacyLevel === 'PRIVATE' && fallbackDataUri )
46
- ship . sponsor . avatarUrl = fallbackDataUri
40
+ const pngBuffer = await fetchImage ( ship . sponsor . avatarUrl ) . catch ( ( e ) => {
41
+ t . error ( `Failed to fetch avatar for ${ ship . sponsor . login || ship . sponsor . name } [${ ship . sponsor . avatarUrl } ]` )
42
+ t . error ( e )
43
+ if ( fallbackAvatar )
44
+ return fallbackAvatar
45
+ throw e
46
+ } )
47
47
48
- if ( pngArrayBuffer ) {
49
- const pngBuffer = Buffer . from ( pngArrayBuffer )
48
+ if ( pngBuffer ) {
50
49
const radius = ship . sponsor . type === 'Organization' ? 0.1 : 0.5
51
- const [
52
- highRes ,
53
- mediumRes ,
54
- lowRes ,
55
- ] = await Promise . all ( [
56
- round ( pngBuffer , radius , 120 ) ,
57
- round ( pngBuffer , radius , 80 ) ,
58
- round ( pngBuffer , radius , 50 ) ,
59
- ] )
60
-
61
- const highResBase64 = highRes . toString ( 'base64' )
62
50
63
- ship . sponsor . avatarBuffer = highResBase64
64
- ship . sponsor . avatarUrlHighRes = highResBase64
65
- ship . sponsor . avatarUrlMediumRes = mediumRes . toString ( 'base64' )
66
- ship . sponsor . avatarUrlLowRes = lowRes . toString ( 'base64' )
51
+ // Store the highest resolution version we use of the original image
52
+ ship . sponsor . avatarBuffer = await round ( pngBuffer , radius , 120 )
67
53
}
68
54
} ) ) )
69
55
}
70
56
57
+ const cache = new Map < string , Map < Buffer , Buffer > > ( )
71
58
export async function round ( image : Buffer , radius = 0.5 , size = 100 ) {
59
+ const cacheKey = `${ radius } :${ size } `
60
+ if ( cache . has ( cacheKey ) ) {
61
+ const cacheHit = cache . get ( cacheKey ) ! . get ( image )
62
+ if ( cacheHit ) {
63
+ return cacheHit
64
+ }
65
+ }
66
+
72
67
const rect = Buffer . from (
73
68
`<svg><rect x="0" y="0" width="${ size } " height="${ size } " rx="${ size * radius } " ry="${ size * radius } "/></svg>` ,
74
69
)
75
70
76
- return await sharp ( image )
71
+ const result = await sharp ( image )
77
72
. resize ( size , size , { fit : sharp . fit . cover } )
78
73
. composite ( [ {
79
74
blend : 'dest-in' ,
@@ -82,6 +77,13 @@ export async function round(image: Buffer, radius = 0.5, size = 100) {
82
77
} ] )
83
78
. png ( { quality : 80 , compressionLevel : 8 } )
84
79
. toBuffer ( )
80
+
81
+ if ( ! cache . has ( cacheKey ) ) {
82
+ cache . set ( cacheKey , new Map ( ) )
83
+ }
84
+ cache . get ( cacheKey ) ! . set ( image , result )
85
+
86
+ return result
85
87
}
86
88
87
89
export function svgToPng ( svg : string ) {
0 commit comments