@@ -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 );
@@ -565,11 +570,8 @@ private static function save_file( $attachment_data, $object_id, $object_type, $
565570 }
566571
567572 // Optimize images (resize and convert to WebP).
568- $ optimized = self ::optimize_image ( $ file_path , $ max_dimension );
569- if ( $ optimized ['changed ' ] ) {
570- $ file_path = $ optimized ['path ' ];
571- $ file_name = \basename ( $ file_path );
572- }
573+ $ file_path = self ::optimize_image ( $ file_path , $ max_dimension );
574+ $ file_name = \basename ( $ file_path );
573575
574576 // Get mime type and validate file.
575577 $ file_info = \wp_check_filetype_and_ext ( $ file_path , $ file_name );
@@ -582,6 +584,32 @@ private static function save_file( $attachment_data, $object_id, $object_type, $
582584 );
583585 }
584586
587+ /**
588+ * Get a unique file path by appending a counter if the file already exists.
589+ *
590+ * @param string $file_path The desired file path.
591+ *
592+ * @return string A unique file path that doesn't exist.
593+ */
594+ private static function get_unique_path ( $ file_path ) {
595+ if ( ! \file_exists ( $ file_path ) ) {
596+ return $ file_path ;
597+ }
598+
599+ $ path_info = \pathinfo ( $ file_path );
600+ $ dir = $ path_info ['dirname ' ];
601+ $ base_name = $ path_info ['filename ' ];
602+ $ extension = isset ( $ path_info ['extension ' ] ) ? '. ' . $ path_info ['extension ' ] : '' ;
603+ $ counter = 1 ;
604+
605+ do {
606+ $ new_path = $ dir . '/ ' . $ base_name . '- ' . $ counter . $ extension ;
607+ ++$ counter ;
608+ } while ( \file_exists ( $ new_path ) );
609+
610+ return $ new_path ;
611+ }
612+
585613 /**
586614 * Optimize an image file by resizing and converting to WebP.
587615 *
@@ -591,32 +619,23 @@ private static function save_file( $attachment_data, $object_id, $object_type, $
591619 * @param string $file_path Path to the image file.
592620 * @param int $max_dimension Maximum width/height in pixels.
593621 *
594- * @return array{path: string, changed: bool}|\WP_Error The optimized file path and whether it changed, or WP_Error .
622+ * @return string The optimized file path.
595623 */
596624 private static function optimize_image ( $ file_path , $ max_dimension ) {
597625 // Check if it's an image.
598626 $ mime_type = \wp_check_filetype ( $ file_path )['type ' ] ?? '' ;
599627 if ( ! $ mime_type || ! \str_starts_with ( $ mime_type , 'image/ ' ) ) {
600- return array (
601- 'path ' => $ file_path ,
602- 'changed ' => false ,
603- );
628+ return $ file_path ;
604629 }
605630
606631 // Skip SVG and GIF files (GIFs may be animated).
607632 if ( \in_array ( $ mime_type , array ( 'image/svg+xml ' , 'image/gif ' ), true ) ) {
608- return array (
609- 'path ' => $ file_path ,
610- 'changed ' => false ,
611- );
633+ return $ file_path ;
612634 }
613635
614636 $ editor = \wp_get_image_editor ( $ file_path );
615637 if ( \is_wp_error ( $ editor ) ) {
616- return array (
617- 'path ' => $ file_path ,
618- 'changed ' => false ,
619- );
638+ return $ file_path ;
620639 }
621640
622641 $ size = $ editor ->get_size ();
@@ -633,29 +652,23 @@ private static function optimize_image( $file_path, $max_dimension ) {
633652 // Determine output format and save.
634653 if ( $ can_webp ) {
635654 // Convert to WebP.
636- $ new_path = \preg_replace ( '/\.[^.]+$/ ' , '.webp ' , $ file_path );
655+ $ new_path = self :: get_unique_path ( \preg_replace ( '/\.[^.]+$/ ' , '.webp ' , $ file_path ) );
637656 $ result = $ editor ->save ( $ new_path , 'image/webp ' );
638657 } elseif ( \in_array ( $ mime_type , array ( 'image/png ' , 'image/webp ' ), true ) ) {
639658 // Keep original format for potentially transparent images when WebP not available.
640659 if ( ! $ needs_resize ) {
641660 // No changes needed.
642- return array (
643- 'path ' => $ file_path ,
644- 'changed ' => false ,
645- );
661+ return $ file_path ;
646662 }
647663 $ result = $ editor ->save ( $ file_path );
648664 } else {
649665 // Convert to JPEG when WebP not available.
650- $ new_path = \preg_replace ( '/\.[^.]+$/ ' , '.jpg ' , $ file_path );
666+ $ new_path = self :: get_unique_path ( \preg_replace ( '/\.[^.]+$/ ' , '.jpg ' , $ file_path ) );
651667 $ result = $ editor ->save ( $ new_path , 'image/jpeg ' );
652668 }
653669
654670 if ( \is_wp_error ( $ result ) ) {
655- return array (
656- 'path ' => $ file_path ,
657- 'changed ' => false ,
658- );
671+ return $ file_path ;
659672 }
660673
661674 // Handle result - $result is always an array from $editor->save().
@@ -666,10 +679,7 @@ private static function optimize_image( $file_path, $max_dimension ) {
666679 \wp_delete_file ( $ file_path );
667680 }
668681
669- return array (
670- 'path ' => $ result_path ,
671- 'changed ' => true ,
672- );
682+ return $ result_path ;
673683 }
674684
675685 /**
0 commit comments