diff --git a/includes/Classifai/Command/ClassifaiCommand.php b/includes/Classifai/Command/ClassifaiCommand.php index 37f922fb9..0f37e7e21 100644 --- a/includes/Classifai/Command/ClassifaiCommand.php +++ b/includes/Classifai/Command/ClassifaiCommand.php @@ -7,6 +7,7 @@ use Classifai\Watson\Normalizer; use Classifai\PostClassifier; use Classifai\Providers\Azure\ComputerVision; +use Classifai\Providers\Azure\SmartCropping; /** * ClassifaiCommand is the command line interface of the ClassifAI plugin. @@ -253,8 +254,9 @@ public function image( $args = [], $opts = [] ) { $limit_total = min( $total, intval( $opts['limit'] ) ); } - $errors = []; - $message = "Classifying $limit_total images ..."; + $errors = []; + $message = "Classifying $limit_total images ..."; + $progress_bar = \WP_CLI\Utils\make_progress_bar( $message, $limit_total ); for ( $index = 0; $index < $limit_total; $index++ ) { @@ -280,6 +282,109 @@ public function image( $args = [], $opts = [] ) { } } + /** + * Batch crop image(s). + * + * ## Options + * + * [] + * : Comma delimited Attachment IDs to crop. + * + * [--limit=] + * : Limit classification to N attachments. Default 100. + * + * [--skip=] + * : Skip first N attachments. Default false. + * + * @param array $args Arguments. + * @param array $opts Options. + */ + public function crop( $args = [], $opts = [] ) { + $classifier = new ComputerVision( false ); + $settings = $classifier->get_settings(); + $smart_cropping = new SmartCropping( $settings ); + + if ( ! isset( $settings['enable_smart_cropping'] ) || '1' !== $settings['enable_smart_cropping'] ) { + \WP_CLI::error( 'Smart Cropping is disabled. Enable it in Image settings to use this command.' ); + } + + $default_opts = [ + 'limit' => false, + ]; + + $opts = wp_parse_args( $opts, $default_opts ); + + if ( ! empty( $args[0] ) ) { + $attachment_ids = explode( ',', $args[0] ); + } else { + $attachment_ids = $this->get_attachment_to_classify( array_merge( $opts, [ 'force' => true ] ) ); + } + + $total = count( $attachment_ids ); + + if ( empty( $total ) ) { + return \WP_CLI::log( 'No images to crop.' ); + } + + $limit_total = $total; + if ( $opts['limit'] ) { + $limit_total = min( $total, intval( $opts['limit'] ) ); + } + + $errors = []; + $message = "Cropping $limit_total images ..."; + + $progress_bar = \WP_CLI\Utils\make_progress_bar( $message, $limit_total ); + + for ( $index = 0; $index < $limit_total; $index++ ) { + $attachment_id = $attachment_ids[ $index ]; + + $progress_bar->tick(); + + $current_meta = wp_get_attachment_metadata( $attachment_id ); + + foreach ( $current_meta['sizes'] as $size => $size_data ) { + if ( ! $smart_cropping->should_crop( $size ) ) { + continue; + } + + $data = [ + 'width' => $size_data['width'], + 'height' => $size_data['height'], + ]; + + $smart_thumbnail = $smart_cropping->get_cropped_thumbnail( $attachment_id, $data ); + + if ( is_wp_error( $smart_thumbnail ) ) { + $errors[ $attachment_id . ':' . $size_data['width'] . 'x' . $size_data['height'] ] = $smart_thumbnail; + } + } + } + + $progress_bar->finish(); + + $total_errors = count( $errors ); + $total_success = $total - $total_errors; + + foreach ( $errors as $attachment_id => $error ) { + \WP_CLI::log( + sprintf( + '%1$s: %2$s (%3$s).', + $attachment_id, + $error->get_error_message(), + $error->get_error_code() + ) + ); + } + + if ( $total_success > 0 ) { + \WP_CLI::success( "Cropped $total_success images, $total_errors errors." ); + } else { + \WP_CLI::error( "Cropped $total_success images, $total_errors errors." ); + } + + } + /** * Prints the Basic Auth header based on credentials configured in * the plugin. @@ -391,12 +496,12 @@ private function get_attachment_to_classify( $opts = [] ) { ]; } - \WP_CLI::log( 'Fetching images to classify ...' ); + \WP_CLI::log( 'Fetching images ...' ); $query = new \WP_Query( $query_params ); $images = $query->posts; - \WP_CLI::log( 'Fetching images to classify ... DONE (' . count( $images ) . ')' ); + \WP_CLI::log( 'Fetching images ... DONE (' . count( $images ) . ')' ); return $images; } diff --git a/includes/Classifai/Providers/Azure/SmartCropping.php b/includes/Classifai/Providers/Azure/SmartCropping.php index bee7bc297..b21e158a5 100644 --- a/includes/Classifai/Providers/Azure/SmartCropping.php +++ b/includes/Classifai/Providers/Azure/SmartCropping.php @@ -182,7 +182,8 @@ public function generate_attachment_metadata( $metadata, $attachment_id ) { ]; $better_thumb_filename = $this->get_cropped_thumbnail( $attachment_id, $data ); - if ( ! empty( $better_thumb_filename ) ) { + + if ( ! empty( $better_thumb_filename ) && ! is_wp_error( $better_thumb_filename ) ) { $metadata['sizes'][ $size ]['file'] = basename( $better_thumb_filename ); } } @@ -235,6 +236,11 @@ public function get_cropped_thumbnail( $attachment_id, $size_data ) { $new_thumb_image = $this->request_cropped_thumbnail( $data ); set_transient( 'classifai_azure_computer_vision_smart_cropping_latest_response', $new_thumb_image, DAY_IN_SECONDS * 30 ); + + if ( is_wp_error( $new_thumb_image ) ) { + return $new_thumb_image; + } + if ( empty( $new_thumb_image ) ) { return false; } @@ -334,10 +340,14 @@ public function request_cropped_thumbnail( $data ) { */ do_action( 'classifai_smart_cropping_after_request', $response, $url, $data ); + $response_body = wp_remote_retrieve_body( $response ); + if ( 200 === wp_remote_retrieve_response_code( $response ) ) { - return wp_remote_retrieve_body( $response ); + return $response_body; } + $response_json = json_decode( $response_body ); + /** * Fires when the generateThumbnail smart-cropping API response did not have a 200 status code. * @@ -350,6 +360,14 @@ public function request_cropped_thumbnail( $data ) { */ do_action( 'classifai_smart_cropping_unsuccessful_response', $response, $url, $data ); + if ( ! empty( $response_json->code ) ) { + return new \WP_Error( $response_json->code, $response_json->message ); + } + + if ( ! empty( $response_json->errors ) ) { + return new \WP_Error( 'classifai_smart_cropping_api_validation_errors', implode( ' ', $response_json->errors->smartCropping ) ); + } + return false; } }