diff --git a/wordpress.org/public_html/wp-content/plugins/photo-directory/inc/cli.php b/wordpress.org/public_html/wp-content/plugins/photo-directory/inc/cli.php
index 57acb71fa7..dd1d084f6a 100644
--- a/wordpress.org/public_html/wp-content/plugins/photo-directory/inc/cli.php
+++ b/wordpress.org/public_html/wp-content/plugins/photo-directory/inc/cli.php
@@ -157,6 +157,134 @@ public function init( $args, $assoc_args ) {
}
}
+ /**
+ * Points scaled-down photos back at their original files.
+ *
+ * Photos uploaded while a lower 'big_image_size_threshold' was in effect
+ * have a "-scaled" copy as their attached file, so their "Original Size"
+ * download served the copy. The original file is still stored next to it,
+ * and the "-scaled" copy is left in place.
+ *
+ * ## OPTIONS
+ *
+ * [--dry-run]
+ * : List the photos that would be updated without changing them.
+ *
+ * ## EXAMPLES
+ *
+ * # Preview which photos would be updated.
+ * $ wp photos restore-originals --dry-run
+ *
+ * # Update them.
+ * $ wp photos restore-originals
+ *
+ * @param array $args Positional arguments. Unused.
+ * @param array $assoc_args Associative arguments.
+ *
+ * @subcommand restore-originals
+ * @when after_wp_load
+ */
+ public function restore_originals( $args, $assoc_args ) {
+ require_once ABSPATH . 'wp-admin/includes/image.php';
+
+ $dry_run = $assoc_args['dry-run'] ?? false;
+ $threshold = Uploads::big_image_size_threshold( 0 );
+ $updated = 0;
+
+ $attachment_ids = get_posts(
+ [
+ 'post_type' => 'attachment',
+ 'post_status' => 'inherit',
+ 'posts_per_page' => -1,
+ 'fields' => 'ids',
+ 'meta_query' => [
+ [
+ 'key' => '_wp_attachment_metadata',
+ 'value' => 'original_image',
+ 'compare' => 'LIKE',
+ ],
+ ],
+ ]
+ );
+
+ foreach ( $attachment_ids as $attachment_id ) {
+ $parent = get_post_parent( $attachment_id );
+ if ( ! $parent || Registrations::get_post_type() !== $parent->post_type ) {
+ continue;
+ }
+
+ $meta = wp_get_attachment_metadata( $attachment_id );
+ if ( empty( $meta['original_image'] ) || empty( $meta['file'] ) ) {
+ continue;
+ }
+
+ // Core also sets 'original_image' for copies that were only EXIF-rotated.
+ if ( ! str_contains( wp_basename( $meta['file'] ), '-scaled.' ) ) {
+ continue;
+ }
+
+ $original_path = wp_get_original_image_path( $attachment_id );
+ $image_size = $original_path ? wp_getimagesize( $original_path ) : false;
+
+ if ( ! $image_size ) {
+ WP_CLI::warning( "Photo {$parent->ID}: original file {$meta['original_image']} could not be read." );
+ continue;
+ }
+
+ // Still above the current threshold, so scaling it was intended.
+ if ( max( $image_size[0], $image_size[1] ) > $threshold ) {
+ continue;
+ }
+
+ // Core rotates the copy per the original's EXIF orientation, but stores the original unrotated.
+ $exif = wp_read_image_metadata( $original_path );
+ if ( ( $exif['orientation'] ?? 0 ) > 1 || ( $meta['width'] > $meta['height'] ) !== ( $image_size[0] > $image_size[1] ) ) {
+ WP_CLI::warning( "Photo {$parent->ID}: {$meta['file']} was rotated from its original, skipping." );
+ continue;
+ }
+
+ $file = path_join( dirname( $meta['file'] ), $meta['original_image'] );
+
+ WP_CLI::log( "Photo {$parent->ID}: {$meta['file']} ({$meta['width']}x{$meta['height']}) -> {$file} ({$image_size[0]}x{$image_size[1]})" );
+ ++$updated;
+
+ if ( $dry_run ) {
+ continue;
+ }
+
+ update_attached_file( $attachment_id, $file );
+
+ // Keep the "-scaled" copy tracked so deleting the photo deletes it too.
+ $meta['sizes']['scaled'] = [
+ 'file' => wp_basename( $meta['file'] ),
+ 'width' => $meta['width'],
+ 'height' => $meta['height'],
+ 'mime-type' => get_post_mime_type( $attachment_id ),
+ ];
+
+ $meta['file'] = $file;
+ $meta['width'] = $image_size[0];
+ $meta['height'] = $image_size[1];
+ $meta['filesize'] = wp_filesize( $original_path );
+ unset( $meta['original_image'] );
+
+ if ( isset( $meta['gcs_name'] ) ) {
+ $meta['gcs_name'] = $file;
+ }
+ if ( isset( $meta['gcs_url'] ) ) {
+ $meta['gcs_url'] = wp_get_attachment_url( $attachment_id );
+ }
+
+ wp_update_attachment_metadata( $attachment_id, $meta );
+ }
+
+ if ( $dry_run ) {
+ WP_CLI::success( "{$updated} photo(s) would be updated." );
+ } else {
+ WP_CLI::success( "{$updated} photo(s) updated." );
+ }
+ }
+
}
WP_CLI::add_command( 'photos', 'WordPressdotorg\Photo_Directory\CLI' );