@@ -98,6 +98,26 @@ impl OgImageGenerator {
98
98
Self :: default ( )
99
99
}
100
100
101
+ /// Detects the image format from the first few bytes using magic numbers.
102
+ ///
103
+ /// Returns the appropriate file extension for supported formats:
104
+ /// - PNG: returns "png"
105
+ /// - JPEG: returns "jpg"
106
+ /// - Unsupported formats: returns None
107
+ fn detect_image_format ( bytes : & [ u8 ] ) -> Option < & ' static str > {
108
+ // PNG magic number: 89 50 4E 47 0D 0A 1A 0A
109
+ if bytes. starts_with ( & [ 0x89 , 0x50 , 0x4E , 0x47 , 0x0D , 0x0A , 0x1A , 0x0A ] ) {
110
+ return Some ( "png" ) ;
111
+ }
112
+
113
+ // JPEG magic number: FF D8 FF
114
+ if bytes. starts_with ( & [ 0xFF , 0xD8 , 0xFF ] ) {
115
+ return Some ( "jpg" ) ;
116
+ }
117
+
118
+ None
119
+ }
120
+
101
121
/// Creates a new `OgImageGenerator` using the `TYPST_PATH` environment variable.
102
122
///
103
123
/// If the `TYPST_PATH` environment variable is set, uses that path.
@@ -218,13 +238,9 @@ impl OgImageGenerator {
218
238
let client = reqwest:: Client :: new ( ) ;
219
239
for ( index, author) in data. authors . iter ( ) . enumerate ( ) {
220
240
if let Some ( avatar) = & author. avatar {
221
- let filename = format ! ( "avatar_{index}.png" ) ;
222
- let avatar_path = assets_dir. join ( & filename) ;
223
-
224
241
debug ! (
225
242
author_name = %author. name,
226
243
avatar_url = %avatar,
227
- avatar_path = %avatar_path. display( ) ,
228
244
"Processing avatar for author {}" , author. name
229
245
) ;
230
246
@@ -271,6 +287,32 @@ impl OgImageGenerator {
271
287
bytes
272
288
} ;
273
289
290
+ // Detect the image format and determine the appropriate file extension
291
+ let Some ( extension) = Self :: detect_image_format ( & bytes) else {
292
+ // Format not supported, log warning with first 20 bytes for debugging
293
+ let debug_bytes = & bytes[ ..bytes. len ( ) . min ( 20 ) ] ;
294
+ let hex_bytes = debug_bytes
295
+ . iter ( )
296
+ . map ( |b| format ! ( "{b:02x}" ) )
297
+ . collect :: < Vec < _ > > ( )
298
+ . join ( " " ) ;
299
+
300
+ warn ! ( "Unsupported avatar format at {avatar}, first 20 bytes: {hex_bytes}" ) ;
301
+
302
+ // Skip this avatar and continue with the next one
303
+ continue ;
304
+ } ;
305
+
306
+ let filename = format ! ( "avatar_{index}.{extension}" ) ;
307
+ let avatar_path = assets_dir. join ( & filename) ;
308
+
309
+ debug ! (
310
+ author_name = %author. name,
311
+ avatar_url = %avatar,
312
+ avatar_path = %avatar_path. display( ) ,
313
+ "Writing avatar file with detected format"
314
+ ) ;
315
+
274
316
// Write the bytes to the avatar file
275
317
fs:: write ( & avatar_path, & bytes) . await . map_err ( |err| {
276
318
OgImageError :: AvatarWriteError {
0 commit comments