@@ -427,13 +427,14 @@ private static function save_attachment( $attachment_data, $post_id, $author_id
427427 require_once ABSPATH . 'wp-admin/includes/image.php ' ;
428428 }
429429
430+ // Initialize filesystem.
431+ \WP_Filesystem ();
432+ global $ wp_filesystem ;
433+
430434 $ is_local = ! preg_match ( '#^https?://#i ' , $ attachment_data ['url ' ] );
431435
432436 if ( $ is_local ) {
433437 // Read local file from disk.
434- \WP_Filesystem ();
435- global $ wp_filesystem ;
436-
437438 if ( ! $ wp_filesystem ->exists ( $ attachment_data ['url ' ] ) ) {
438439 /* translators: %s: file path */
439440 return new \WP_Error ( 'file_not_found ' , sprintf ( \__ ( 'File not found: %s ' , 'activitypub ' ), $ attachment_data ['url ' ] ) );
@@ -451,18 +452,24 @@ private static function save_attachment( $attachment_data, $post_id, $author_id
451452 }
452453 }
453454
454- // Optimize images before sideloading (resize and convert to WebP).
455- $ optimized = self ::optimize_image ( $ tmp_file , self ::MAX_IMAGE_DIMENSION );
456- if ( $ optimized ['changed ' ] ) {
457- $ tmp_file = $ optimized ['path ' ];
455+ // Get original filename from URL.
456+ $ original_name = \basename ( \wp_parse_url ( $ attachment_data ['url ' ], PHP_URL_PATH ) );
457+
458+ // Rename temp file to have proper extension for optimize_image to detect mime type.
459+ $ original_ext = \pathinfo ( $ original_name , PATHINFO_EXTENSION );
460+ if ( $ original_ext ) {
461+ $ renamed_tmp = $ tmp_file . '. ' . $ original_ext ;
462+ if ( $ wp_filesystem ->move ( $ tmp_file , $ renamed_tmp , true ) ) {
463+ $ tmp_file = $ renamed_tmp ;
464+ }
458465 }
459466
460- // Prepare file array for WordPress .
461- $ original_name = \basename ( \wp_parse_url ( $ attachment_data [ ' url ' ], PHP_URL_PATH ) );
467+ // Optimize images before sideloading (resize and convert to WebP) .
468+ $ tmp_file = self :: optimize_image ( $ tmp_file , self :: MAX_IMAGE_DIMENSION );
462469
463- // Update filename extension if format changed .
464- if ( $ optimized [ ' changed ' ] ) {
465- $ new_ext = \pathinfo ( $ tmp_file , PATHINFO_EXTENSION );
470+ // Update filename extension to match optimized file .
471+ $ new_ext = \pathinfo ( $ tmp_file , PATHINFO_EXTENSION );
472+ if ( $ new_ext ) {
466473 $ original_name = \preg_replace ( '/\.[^.]+$/ ' , '. ' . $ new_ext , $ original_name );
467474 }
468475
@@ -472,14 +479,12 @@ private static function save_attachment( $attachment_data, $post_id, $author_id
472479 );
473480
474481 // Prepare attachment post data.
475- // Clear mime type if format changed so WordPress auto-detects it.
476- $ mime_type = $ optimized ['changed ' ] ? '' : ( $ attachment_data ['mediaType ' ] ?? '' );
482+ // Let WordPress auto-detect the mime type from the file.
477483 $ post_data = array (
478- 'post_mime_type ' => $ mime_type ,
479- 'post_title ' => $ attachment_data ['name ' ] ?? '' ,
480- 'post_content ' => $ attachment_data ['name ' ] ?? '' ,
481- 'post_author ' => $ author_id ,
482- 'meta_input ' => array (
484+ 'post_title ' => $ attachment_data ['name ' ] ?? '' ,
485+ 'post_content ' => $ attachment_data ['name ' ] ?? '' ,
486+ 'post_author ' => $ author_id ,
487+ 'meta_input ' => array (
483488 '_source_url ' => $ attachment_data ['url ' ],
484489 ),
485490 );
@@ -579,11 +584,8 @@ private static function save_file( $attachment_data, $object_id, $object_type, $
579584 }
580585
581586 // Optimize images (resize and convert to WebP).
582- $ optimized = self ::optimize_image ( $ file_path , $ max_dimension );
583- if ( $ optimized ['changed ' ] ) {
584- $ file_path = $ optimized ['path ' ];
585- $ file_name = \basename ( $ file_path );
586- }
587+ $ file_path = self ::optimize_image ( $ file_path , $ max_dimension );
588+ $ file_name = \basename ( $ file_path );
587589
588590 // Get mime type and validate file.
589591 $ file_info = \wp_check_filetype_and_ext ( $ file_path , $ file_name );
@@ -596,6 +598,32 @@ private static function save_file( $attachment_data, $object_id, $object_type, $
596598 );
597599 }
598600
601+ /**
602+ * Get a unique file path by appending a counter if the file already exists.
603+ *
604+ * @param string $file_path The desired file path.
605+ *
606+ * @return string A unique file path that doesn't exist.
607+ */
608+ private static function get_unique_path ( $ file_path ) {
609+ if ( ! \file_exists ( $ file_path ) ) {
610+ return $ file_path ;
611+ }
612+
613+ $ path_info = \pathinfo ( $ file_path );
614+ $ dir = $ path_info ['dirname ' ];
615+ $ base_name = $ path_info ['filename ' ];
616+ $ extension = isset ( $ path_info ['extension ' ] ) ? '. ' . $ path_info ['extension ' ] : '' ;
617+ $ counter = 1 ;
618+
619+ do {
620+ $ new_path = $ dir . '/ ' . $ base_name . '- ' . $ counter . $ extension ;
621+ ++$ counter ;
622+ } while ( \file_exists ( $ new_path ) );
623+
624+ return $ new_path ;
625+ }
626+
599627 /**
600628 * Optimize an image file by resizing and converting to WebP.
601629 *
@@ -605,32 +633,23 @@ private static function save_file( $attachment_data, $object_id, $object_type, $
605633 * @param string $file_path Path to the image file.
606634 * @param int $max_dimension Maximum width/height in pixels.
607635 *
608- * @return array{path: string, changed: bool}|\WP_Error The optimized file path and whether it changed, or WP_Error .
636+ * @return string The optimized file path.
609637 */
610638 private static function optimize_image ( $ file_path , $ max_dimension ) {
611639 // Check if it's an image.
612640 $ mime_type = \wp_check_filetype ( $ file_path )['type ' ] ?? '' ;
613641 if ( ! $ mime_type || ! \str_starts_with ( $ mime_type , 'image/ ' ) ) {
614- return array (
615- 'path ' => $ file_path ,
616- 'changed ' => false ,
617- );
642+ return $ file_path ;
618643 }
619644
620645 // Skip SVG and GIF files (GIFs may be animated).
621646 if ( \in_array ( $ mime_type , array ( 'image/svg+xml ' , 'image/gif ' ), true ) ) {
622- return array (
623- 'path ' => $ file_path ,
624- 'changed ' => false ,
625- );
647+ return $ file_path ;
626648 }
627649
628650 $ editor = \wp_get_image_editor ( $ file_path );
629651 if ( \is_wp_error ( $ editor ) ) {
630- return array (
631- 'path ' => $ file_path ,
632- 'changed ' => false ,
633- );
652+ return $ file_path ;
634653 }
635654
636655 $ size = $ editor ->get_size ();
@@ -647,29 +666,23 @@ private static function optimize_image( $file_path, $max_dimension ) {
647666 // Determine output format and save.
648667 if ( $ can_webp ) {
649668 // Convert to WebP.
650- $ new_path = \preg_replace ( '/\.[^.]+$/ ' , '.webp ' , $ file_path );
669+ $ new_path = self :: get_unique_path ( \preg_replace ( '/\.[^.]+$/ ' , '.webp ' , $ file_path ) );
651670 $ result = $ editor ->save ( $ new_path , 'image/webp ' );
652671 } elseif ( \in_array ( $ mime_type , array ( 'image/png ' , 'image/webp ' ), true ) ) {
653672 // Keep original format for potentially transparent images when WebP not available.
654673 if ( ! $ needs_resize ) {
655674 // No changes needed.
656- return array (
657- 'path ' => $ file_path ,
658- 'changed ' => false ,
659- );
675+ return $ file_path ;
660676 }
661677 $ result = $ editor ->save ( $ file_path );
662678 } else {
663679 // Convert to JPEG when WebP not available.
664- $ new_path = \preg_replace ( '/\.[^.]+$/ ' , '.jpg ' , $ file_path );
680+ $ new_path = self :: get_unique_path ( \preg_replace ( '/\.[^.]+$/ ' , '.jpg ' , $ file_path ) );
665681 $ result = $ editor ->save ( $ new_path , 'image/jpeg ' );
666682 }
667683
668684 if ( \is_wp_error ( $ result ) ) {
669- return array (
670- 'path ' => $ file_path ,
671- 'changed ' => false ,
672- );
685+ return $ file_path ;
673686 }
674687
675688 // Handle result - $result is always an array from $editor->save().
@@ -680,10 +693,7 @@ private static function optimize_image( $file_path, $max_dimension ) {
680693 \wp_delete_file ( $ file_path );
681694 }
682695
683- return array (
684- 'path ' => $ result_path ,
685- 'changed ' => true ,
686- );
696+ return $ result_path ;
687697 }
688698
689699 /**
0 commit comments