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 +261 -51 1.0.01.7.7 View file →
@@ -1,96 +1,306 @@
1 1 <?php
2 2
3 -namespace ImageOptimizer\Modules\Optimization\Components;
3 +namespace ImageOptimization\Modules\Optimization\Components;
4 4
5 -use ImageOptimizer\Classes\Async_Operation\Async_Operation_Hook;
6 -use ImageOptimizer\Classes\Image\{
5 +use ImageOptimization\Classes\Async_Operation\{
6 + Async_Operation,
7 + Async_Operation_Hook,
8 + Async_Operation_Queue,
9 +};
10 +
11 +use ImageOptimization\Classes\Image\{
7 12 Image_Meta,
8 - Image_Restore,
9 - Image_Status,
13 + Image_Status
10 14 };
11 -use ImageOptimizer\Classes\Logger;
12 -use ImageOptimizer\Modules\Optimization\Classes\Optimize_Image;
13 -use ImageOptimizer\Modules\Optimization\Classes\Bulk_Optimization as Bulk_Optimization_Controller;
15 +
16 +use ImageOptimization\Modules\Optimization\{
17 + Classes\Exceptions\Bulk_Token_Expired_Error,
18 + Classes\Optimize_Image,
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 +};
24 +
25 +use ImageOptimization\Classes\Logger;
26 +use ImageOptimization\Classes\Utils;
27 +use ImageOptimization\Classes\Exceptions\Quota_Exceeded_Error;
14 28 use Throwable;
15 29
30 +// @codeCoverageIgnoreStart
16 31 if ( ! defined( 'ABSPATH' ) ) {
17 32 exit; // Exit if accessed directly.
18 33 }
34 +// @codeCoverageIgnoreEnd
19 35
20 36 class Bulk_Optimization {
21 - const BULK_OPTIMIZATION_BASE_SLUG = 'image-optimizer-bulk-optimization';
22 - const BULK_OPTIMIZATION_CAPABILITY = 'manage_options';
37 + /** @async */
38 + public function optimize_bulk() {
39 + $queue = new Bulk_Optimization_Queue( Bulk_Optimization_Queue_Type::OPTIMIZATION );
23 40
24 - public function render_app() {
25 - ?>
26 - <!-- The hack required to wrap WP notifications -->
27 - <div class="wrap">
28 - <h1 style="display: none;" role="presentation"></h1>
29 - </div>
41 + if ( ! $queue->exists() ) {
42 + Logger::debug( 'Bulk optimization queue did not found' );
30 43
31 - <div id="image-optimizer-app"></div>
32 - <?php
33 - }
44 + return;
45 + }
34 46
35 - public function register_page() {
36 - add_media_page(
37 - '',
38 - __( 'Bulk Optimization', 'image-optimizer' ),
39 - self::BULK_OPTIMIZATION_CAPABILITY,
40 - self::BULK_OPTIMIZATION_BASE_SLUG,
41 - [ $this, 'render_app' ],
42 - 7
43 - );
44 - }
47 + if ( ! $queue->has_more_images() ) {
48 + Logger::debug( 'No more pending images, deleting queue' );
49 + $queue->delete();
45 50
46 - /** @async */
47 - 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 +
48 100 try {
49 - $bulk_token = Bulk_Optimization_Controller::get_bulk_operation_token( $operation_id );
50 -
51 101 $oi = new Optimize_Image(
52 102 $image_id,
53 103 'bulk',
54 - $bulk_token
104 + $queue->get_bulk_token()
55 105 );
56 106
57 107 $oi->optimize();
58 - } catch ( Throwable $t ) {
59 - Logger::log( Logger::LEVEL_ERROR, 'Optimization error. Reason: ' . $t->getMessage() );
60 108
61 - ( new Image_Meta( $image_id ) )
62 - ->set_status( Image_Status::OPTIMIZATION_FAILED )
109 + $queue
110 + ->mark_image_completed( $image_id )
111 + ->increment_optimized_counter()
63 112 ->save();
113 +
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() );
64 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 + }
146 + } catch ( Throwable $t ) {
147 + Logger::error( 'Failed to create next async operation or delete queue: ' . $t->getMessage() );
148 + }
65 149 }
66 150
67 151 /** @async */
68 - public function reoptimize_bulk( int $image_id, string $operation_id ) {
69 - try {
70 - Image_Restore::restore( $image_id, true );
152 + public function reoptimize_bulk() {
153 + $queue = new Bulk_Optimization_Queue( Bulk_Optimization_Queue_Type::REOPTIMIZATION );
71 154
72 - $bulk_token = Bulk_Optimization_Controller::get_bulk_operation_token( $operation_id );
155 + if ( ! $queue->exists() ) {
156 + return;
157 + }
73 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;
191 + }
192 + }
193 +
194 + $image_id = $queue->get_next_image();
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 {
74 209 $oi = new Optimize_Image(
75 210 $image_id,
76 - 'bulk',
77 - $bulk_token
211 + 'bulk-reoptimize',
212 + $queue->get_bulk_token(),
213 + true
78 214 );
79 215
80 216 $oi->optimize();
81 - } catch ( Throwable $t ) {
82 - Logger::log( Logger::LEVEL_ERROR, 'Reoptimization error. Reason: ' . $t->getMessage() );
83 217
84 - ( new Image_Meta( $image_id ) )
85 - ->set_status( Image_Status::OPTIMIZATION_FAILED )
218 + $queue
219 + ->mark_image_completed( $image_id )
220 + ->increment_optimized_counter()
86 221 ->save();
222 +
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() );
87 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 + }
261 + } catch ( Throwable $t ) {
262 + Logger::error( 'Failed to create next async operation or delete queue: ' . $t->getMessage() );
263 + }
88 264 }
89 265
266 + /**
267 + * Renders the bulk optimization notice
268 + *
269 + * @return void
270 + */
271 + public function render_bulk_optimization_notice() {
272 + $queue = new Bulk_Optimization_Queue( Bulk_Optimization_Queue_Type::OPTIMIZATION );
273 + $is_in_progress = $queue->exists();
274 + ?>
275 + <div class="notice notice-info notice image-optimizer__notice image-optimizer__notice--info image-optimizer__notice--bulk-tip"
276 + style="display: <?php echo $is_in_progress ? 'block' : 'none'; ?>">
277 + <p>
278 + <b>
279 + <?php esc_html_e(
280 + 'Heads up!',
281 + 'image-optimization'
282 + ); ?>
283 + </b>
284 +
285 + <span>
286 + <?php esc_html_e(
287 + 'Bulk optimizing may take a lot of processing and server time, depending on the number of images. Your site will still work smoothly until the processing is all done, without any downtime.',
288 + 'image-optimization'
289 + ); ?>
290 + </span>
291 + </p>
292 + </div>
293 + <?php
294 + }
295 +
90 296 public function __construct() {
91 - 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' ] );
92 299
93 - add_action( Async_Operation_Hook::OPTIMIZE_BULK, [ $this, 'optimize_bulk' ], 10, 2 );
94 - add_action( Async_Operation_Hook::REOPTIMIZE_BULK, [ $this, 'reoptimize_bulk' ], 10, 2 );
300 + add_action( 'current_screen', function () {
301 + if ( Utils::is_bulk_optimization_page() ) {
302 + add_filter( 'admin_footer_text', [ $this, 'render_bulk_optimization_notice' ] );
303 + }
304 + } );
95 305 }
96 306 }