@@ -98,6 +98,26 @@ impl OgImageGenerator {
9898 Self :: default ( )
9999 }
100100
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+
101121 /// Creates a new `OgImageGenerator` using the `TYPST_PATH` environment variable.
102122 ///
103123 /// If the `TYPST_PATH` environment variable is set, uses that path.
@@ -218,13 +238,9 @@ impl OgImageGenerator {
218238 let client = reqwest:: Client :: new ( ) ;
219239 for ( index, author) in data. authors . iter ( ) . enumerate ( ) {
220240 if let Some ( avatar) = & author. avatar {
221- let filename = format ! ( "avatar_{index}.png" ) ;
222- let avatar_path = assets_dir. join ( & filename) ;
223-
224241 debug ! (
225242 author_name = %author. name,
226243 avatar_url = %avatar,
227- avatar_path = %avatar_path. display( ) ,
228244 "Processing avatar for author {}" , author. name
229245 ) ;
230246
@@ -271,6 +287,32 @@ impl OgImageGenerator {
271287 bytes
272288 } ;
273289
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+
274316 // Write the bytes to the avatar file
275317 fs:: write ( & avatar_path, & bytes) . await . map_err ( |err| {
276318 OgImageError :: AvatarWriteError {
0 commit comments