PluginProbe
Image Optimizer – Compress Images and Convert to WebP or AVIF / 1.7.7
Image Optimizer – Compress Images and Convert to WebP or AVIF v1.7.7
1.7.7 1.7.6 1.7.5 1.7.4 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.2.1 1.3.0 1.4.0 1.4.1 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 All 33 releases
← All changes | modules/optimization/components/bulk-optimization.php +218 -96 1.2.11.7.7 View file →
@@ -1,139 +1,266 @@
1 1 <?php
2 2
3 3 namespace ImageOptimization\Modules\Optimization\Components;
4 4
5 -use ImageOptimization\Classes\Async_Operation\Async_Operation_Hook;
5 +use ImageOptimization\Classes\Async_Operation\{
6 + Async_Operation,
7 + Async_Operation_Hook,
8 + Async_Operation_Queue,
9 +};
10 +
6 11 use ImageOptimization\Classes\Image\{
7 - Image,
8 12 Image_Meta,
9 - Image_Optimization_Error_Type,
10 - Image_Restore,
11 13 Image_Status
12 14 };
13 -use ImageOptimization\Classes\Async_Operation\Exceptions\Async_Operation_Exception;
14 -use ImageOptimization\Classes\Logger;
15 -use ImageOptimization\Classes\Utils;
16 -use ImageOptimization\Modules\Oauth\Classes\Exceptions\Quota_Exceeded_Error;
15 +
17 16 use ImageOptimization\Modules\Optimization\{
18 17 Classes\Exceptions\Bulk_Token_Expired_Error,
19 - Classes\Exceptions\Image_File_Already_Exists_Error,
20 18 Classes\Optimize_Image,
21 - Classes\Bulk_Optimization_Controller,
22 - Components\Exceptions\Bulk_Optimization_Token_Not_Found_Error,
19 + Classes\Bulk_Optimization\Bulk_Optimization_Queue,
20 + Classes\Bulk_Optimization\Bulk_Optimization_Queue_Status,
21 + Classes\Bulk_Optimization\Bulk_Optimization_Queue_Type,
22 + Classes\Bulk_Optimization\Bulk_Optimization_Token_Manager,
23 23 };
24 24
25 +use ImageOptimization\Classes\Logger;
26 +use ImageOptimization\Classes\Utils;
27 +use ImageOptimization\Classes\Exceptions\Quota_Exceeded_Error;
25 28 use Throwable;
26 29
30 +// @codeCoverageIgnoreStart
27 31 if ( ! defined( 'ABSPATH' ) ) {
28 32 exit; // Exit if accessed directly.
29 33 }
34 +// @codeCoverageIgnoreEnd
30 35
31 36 class Bulk_Optimization {
32 - const BULK_OPTIMIZATION_BASE_SLUG = 'image-optimization-bulk-optimization';
33 - const BULK_OPTIMIZATION_CAPABILITY = 'manage_options';
37 + /** @async */
38 + public function optimize_bulk() {
39 + $queue = new Bulk_Optimization_Queue( Bulk_Optimization_Queue_Type::OPTIMIZATION );
34 40
35 - public function render_app() {
36 - ?>
37 - <!-- The hack required to wrap WP notifications -->
38 - <div class="wrap">
39 - <h1 style="display: none;" role="presentation"></h1>
40 - </div>
41 + if ( ! $queue->exists() ) {
42 + Logger::debug( 'Bulk optimization queue did not found' );
41 43
42 - <div id="image-optimization-app"></div>
43 - <?php
44 - }
44 + return;
45 + }
45 46
46 - public function register_page() {
47 - add_media_page(
48 - __( 'Bulk Optimization', 'image-optimization' ),
49 - __( 'Bulk Optimization', 'image-optimization' ),
50 - self::BULK_OPTIMIZATION_CAPABILITY,
51 - self::BULK_OPTIMIZATION_BASE_SLUG,
52 - [ $this, 'render_app' ],
53 - 7
54 - );
55 - }
47 + if ( ! $queue->has_more_images() ) {
48 + Logger::debug( 'No more pending images, deleting queue' );
49 + $queue->delete();
56 50
57 - /** @async */
58 - public function optimize_bulk( int $image_id, string $operation_id ) {
51 + return;
52 + }
53 +
54 + if ( $queue->should_refresh_token() ) {
55 + Logger::debug( 'Refreshing bulk token' );
56 +
57 + try {
58 + $token_data = Bulk_Optimization_Token_Manager::obtain_token(
59 + $queue->get_stats()[ Bulk_Optimization_Queue_Status::PENDING ],
60 + $queue->get_max_batch_size()
61 + );
62 +
63 + $queue
64 + ->set_bulk_token(
65 + $token_data['token'],
66 + time() + HOUR_IN_SECONDS,
67 + $token_data['batch_size']
68 + )
69 + ->save();
70 + } catch ( Throwable $t ) {
71 + Logger::info( 'Failed to obtain a bulk token: ' . $t->getMessage() );
72 +
73 + foreach ( $queue->get_images_by_status( Bulk_Optimization_Queue_Status::PENDING ) as $image ) {
74 + ( new Image_Meta( $image['id'] ) )
75 + ->set_status( Image_Status::NOT_OPTIMIZED )
76 + ->save();
77 + }
78 +
79 + $queue->delete();
80 +
81 + return;
82 + }
83 + }
84 +
85 + $image_id = $queue->get_next_image();
86 +
87 + Logger::debug( 'Processing image: ' . $image_id );
88 +
89 + if ( ! $image_id ) {
90 + Logger::debug( 'No image to process, deleting queue' );
91 + $queue->delete();
92 +
93 + return;
94 + }
95 +
96 + $queue
97 + ->set_current_image_id( $image_id )
98 + ->save();
99 +
59 100 try {
60 - $bulk_token = Bulk_Optimization_Controller::get_bulk_operation_token( $operation_id );
61 -
62 101 $oi = new Optimize_Image(
63 102 $image_id,
64 103 'bulk',
65 - $bulk_token
104 + $queue->get_bulk_token()
66 105 );
67 106
68 107 $oi->optimize();
69 - } catch ( Quota_Exceeded_Error $qe ) {
70 - ( new Image_Meta( $image_id ) )
71 - ->set_status( Image_Status::OPTIMIZATION_FAILED )
72 - ->set_error_type( Image_Optimization_Error_Type::QUOTA_EXCEEDED )
108 +
109 + $queue
110 + ->mark_image_completed( $image_id )
111 + ->increment_optimized_counter()
73 112 ->save();
74 - } catch ( Image_File_Already_Exists_Error $fe ) {
75 - ( new Image_Meta( $image_id ) )
76 - ->set_status( Image_Status::OPTIMIZATION_FAILED )
77 - ->set_error_type( Image_Optimization_Error_Type::FILE_ALREADY_EXISTS )
78 - ->save();
79 - } catch ( Bulk_Token_Expired_Error | Bulk_Optimization_Token_Not_Found_Error $bte ) {
80 - ( new Image_Meta( $image_id ) )
81 - ->set_status( Image_Status::NOT_OPTIMIZED )
82 - ->save();
83 113
84 - Bulk_Optimization_Controller::reschedule_bulk_optimization();
114 + } catch ( Bulk_Token_Expired_Error $btee ) {
115 + $queue->mark_image_failed( $image_id )->save();
116 +
117 + Logger::info( "Token expired while optimizing image {$image_id}" );
118 + } catch ( Quota_Exceeded_Error $qee ) {
119 + $queue->mark_image_failed( $image_id )->save();
120 +
121 + Logger::info( "Quota exceeded while optimizing image {$image_id}" );
122 + } catch ( Throwable $e ) {
123 + $queue->mark_image_failed( $image_id )->save();
124 +
125 + Logger::info( "Failed to optimize image {$image_id}: " . $e->getMessage() );
126 + }
127 +
128 + $queue
129 + ->set_current_image_id( null )
130 + ->save();
131 +
132 + try {
133 + if ( $queue->has_more_images() ) {
134 + Async_Operation::create(
135 + Async_Operation_Hook::OPTIMIZE_BULK,
136 + [ 'operation_id' => $queue->get_operation_id() ],
137 + Async_Operation_Queue::OPTIMIZE
138 + );
139 +
140 + Logger::debug( 'Async operation created' );
141 + } else {
142 + Logger::debug( 'All images were optimized, deleting queue' );
143 +
144 + $queue->delete();
145 + }
85 146 } catch ( Throwable $t ) {
86 - Logger::log( Logger::LEVEL_ERROR, 'Optimization error. Reason: ' . $t->getMessage() );
87 -
88 - ( new Image_Meta( $image_id ) )
89 - ->set_status( Image_Status::OPTIMIZATION_FAILED )
90 - ->set_error_type( Image_Optimization_Error_Type::GENERIC )
91 - ->save();
147 + Logger::error( 'Failed to create next async operation or delete queue: ' . $t->getMessage() );
92 148 }
93 149 }
94 150
95 151 /** @async */
96 - public function reoptimize_bulk( int $image_id, string $operation_id ) {
97 - try {
98 - $image = new Image( $image_id );
152 + public function reoptimize_bulk() {
153 + $queue = new Bulk_Optimization_Queue( Bulk_Optimization_Queue_Type::REOPTIMIZATION );
99 154
100 - if ( $image->can_be_restored() ) {
101 - Image_Restore::restore( $image_id, true );
155 + if ( ! $queue->exists() ) {
156 + return;
157 + }
158 +
159 + if ( ! $queue->has_more_images() ) {
160 + $queue->delete();
161 +
162 + return;
163 + }
164 +
165 + if ( $queue->should_refresh_token() ) {
166 + try {
167 + $token_data = Bulk_Optimization_Token_Manager::obtain_token(
168 + $queue->get_stats()[ Bulk_Optimization_Queue_Status::PENDING ],
169 + $queue->get_max_batch_size()
170 + );
171 +
172 + $queue
173 + ->set_bulk_token(
174 + $token_data['token'],
175 + time() + HOUR_IN_SECONDS,
176 + $token_data['batch_size']
177 + )
178 + ->save();
179 + } catch ( Throwable $t ) {
180 + Logger::info( 'Failed to obtain bulk token: ' . $t->getMessage() );
181 +
182 + foreach ( $queue->get_images_by_status( Bulk_Optimization_Queue_Status::PENDING ) as $image ) {
183 + ( new Image_Meta( $image['id'] ) )
184 + ->set_status( Image_Status::OPTIMIZATION_FAILED )
185 + ->save();
186 + }
187 +
188 + $queue->delete();
189 +
190 + return;
102 191 }
192 + }
103 193
104 - $bulk_token = Bulk_Optimization_Controller::get_bulk_operation_token( $operation_id );
194 + $image_id = $queue->get_next_image();
105 195
196 + if ( ! $image_id ) {
197 + $queue
198 + ->set_status( Bulk_Optimization_Queue_Status::COMPLETED )
199 + ->save();
200 +
201 + return;
202 + }
203 +
204 + $queue
205 + ->set_current_image_id( $image_id )
206 + ->save();
207 +
208 + try {
106 209 $oi = new Optimize_Image(
107 210 $image_id,
108 - 'bulk',
109 - $bulk_token
211 + 'bulk-reoptimize',
212 + $queue->get_bulk_token(),
213 + true
110 214 );
111 215
112 216 $oi->optimize();
113 - } catch ( Quota_Exceeded_Error $qe ) {
114 - ( new Image_Meta( $image_id ) )
115 - ->set_status( Image_Status::REOPTIMIZING_FAILED )
116 - ->set_error_type( Image_Optimization_Error_Type::QUOTA_EXCEEDED )
217 +
218 + $queue
219 + ->mark_image_completed( $image_id )
220 + ->increment_optimized_counter()
117 221 ->save();
118 - } catch ( Image_File_Already_Exists_Error $fe ) {
119 - ( new Image_Meta( $image_id ) )
120 - ->set_status( Image_Status::REOPTIMIZING_FAILED )
121 - ->set_error_type( Image_Optimization_Error_Type::FILE_ALREADY_EXISTS )
122 - ->save();
123 - } catch ( Bulk_Token_Expired_Error | Bulk_Optimization_Token_Not_Found_Error $bte ) {
124 - ( new Image_Meta( $image_id ) )
125 - ->set_status( Image_Status::NOT_OPTIMIZED )
126 - ->save();
127 222
128 - Bulk_Optimization_Controller::reschedule_bulk_reoptimization();
223 + } catch ( Bulk_Token_Expired_Error $btee ) {
224 + $queue->mark_image_failed( $image_id )->save();
225 +
226 + Logger::info( "Token expired while reoptimizing image {$image_id}" );
227 + } catch ( Quota_Exceeded_Error $qee ) {
228 + $queue->mark_image_failed( $image_id )->save();
229 +
230 + foreach ( $queue->get_images_by_status( Bulk_Optimization_Queue_Status::PENDING ) as $image ) {
231 + ( new Image_Meta( $image['id'] ) )
232 + ->set_status( Image_Status::OPTIMIZATION_FAILED )
233 + ->save();
234 + }
235 +
236 + $queue->delete();
237 +
238 + Logger::info( "Quota exceeded while reoptimizing image {$image_id}" );
239 +
240 + return;
241 + } catch ( Throwable $e ) {
242 + $queue->mark_image_failed( $image_id )->save();
243 +
244 + Logger::info( "Failed to reoptimize image {$image_id}: " . $e->getMessage() );
245 + }
246 +
247 + $queue
248 + ->set_current_image_id( null )
249 + ->save();
250 +
251 + try {
252 + if ( $queue->has_more_images() ) {
253 + Async_Operation::create(
254 + Async_Operation_Hook::REOPTIMIZE_BULK,
255 + [ 'operation_id' => $queue->get_operation_id() ],
256 + Async_Operation_Queue::OPTIMIZE
257 + );
258 + } else {
259 + $queue->delete();
260 + }
129 261 } catch ( Throwable $t ) {
130 - Logger::log( Logger::LEVEL_ERROR, 'Reoptimization error. Reason: ' . $t->getMessage() );
131 -
132 - ( new Image_Meta( $image_id ) )
133 - ->set_status( Image_Status::REOPTIMIZING_FAILED )
134 - ->set_error_type( Image_Optimization_Error_Type::GENERIC )
135 - ->save();
262 + Logger::error( 'Failed to create next async operation or delete queue: ' . $t->getMessage() );
136 263 }
137 264 }
138 265
139 266 /**
@@ -141,13 +268,10 @@
141 268 *
142 269 * @return void
143 270 */
144 271 public function render_bulk_optimization_notice() {
145 - try {
146 - $is_in_progress = Bulk_Optimization_Controller::is_optimization_in_progress();
147 - } catch ( Async_Operation_Exception $aoe ) {
148 - $is_in_progress = false;
149 - }
272 + $queue = new Bulk_Optimization_Queue( Bulk_Optimization_Queue_Type::OPTIMIZATION );
273 + $is_in_progress = $queue->exists();
150 274 ?>
151 275 <div class="notice notice-info notice image-optimizer__notice image-optimizer__notice--info image-optimizer__notice--bulk-tip"
152 276 style="display: <?php echo $is_in_progress ? 'block' : 'none'; ?>">
153 277 <p>
@@ -169,16 +293,14 @@
169 293 <?php
170 294 }
171 295
172 296 public function __construct() {
173 - add_action( 'admin_menu', [ $this, 'register_page' ] );
297 + add_action( Async_Operation_Hook::OPTIMIZE_BULK, [ $this, 'optimize_bulk' ] );
298 + add_action( Async_Operation_Hook::REOPTIMIZE_BULK, [ $this, 'reoptimize_bulk' ] );
174 299
175 - add_action( Async_Operation_Hook::OPTIMIZE_BULK, [ $this, 'optimize_bulk' ], 10, 2 );
176 - add_action( Async_Operation_Hook::REOPTIMIZE_BULK, [ $this, 'reoptimize_bulk' ], 10, 2 );
177 -
178 - add_action('current_screen', function () {
300 + add_action( 'current_screen', function () {
179 301 if ( Utils::is_bulk_optimization_page() ) {
180 302 add_filter( 'admin_footer_text', [ $this, 'render_bulk_optimization_notice' ] );
181 303 }
182 - });
304 + } );
183 305 }
184 306 }