PluginProbe ʕ •ᴥ•ʔ
Robin Image Optimizer – Unlimited Image Optimization, WebP & AVIF / 2.0.6
Robin Image Optimizer – Unlimited Image Optimization, WebP & AVIF v2.0.6
2.0.6 2.0.5 trunk 1.3.7 1.4.0 1.4.1 1.4.2 1.4.6 1.5.0 1.5.3 1.5.6 1.5.8 1.6.5 1.6.6 1.6.9 1.7.0 1.7.4 1.8.1 1.8.2 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4
robin-image-optimizer / includes / classes / class-rio-media-library.php
robin-image-optimizer / includes / classes Last commit date
models 1 month ago processing 5 months ago processors 5 months ago class-rio-attachment.php 5 months ago class-rio-backup.php 7 months ago class-rio-bulk-optimization.php 1 month ago class-rio-cron.php 7 months ago class-rio-image-query.php 5 months ago class-rio-image-statistic.php 5 months ago class-rio-media-library.php 1 month ago class-rio-multisite.php 7 months ago class-rio-optimization-orchestrator.php 5 months ago class-rio-optimization-tools.php 7 months ago class-rio-views.php 5 months ago class-wrio-license.php 7 months ago class-wrio-premium-provider.php 7 months ago class-wrio-support.php 7 months ago index.php 7 months ago
class-rio-media-library.php
959 lines
1 <?php
2
3 // Exit if accessed directly
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 /**
9 * Класс для работы с WordPress media library.
10 *
11 * @version 1.0
12 */
13 class WRIO_Media_Library {
14
15 /**
16 * The single instance of the class.
17 *
18 * @since 1.3.0
19 * @access protected
20 * @var WRIO_Media_Library
21 */
22 protected static $_instance;
23
24 /**
25 * @var array Массив для �
26 ранения объектов WIO_Attachment
27 */
28 private $attachments = [];
29
30 /**
31 * @return WRIO_Media_Library Main instance.
32 * @since 1.3.0
33 */
34 public static function get_instance() {
35 if ( ! isset( static::$_instance ) ) {
36 static::$_instance = new static();
37 }
38
39 return static::$_instance;
40 }
41
42 /**
43 * Установка �
44 уков
45 */
46 public function initHooks() {
47 // оптимизация при загрузке в медиабиблиотеку
48 if ( WRIO_Plugin::app()->getPopulateOption( 'auto_optimize_when_upload', false ) ) {
49 add_filter( 'wp_generate_attachment_metadata', 'WRIO_Media_Library::optimize_after_upload', 10, 2 );
50 add_action( 'wr2x_retina_file_added', 'WRIO_Media_Library::optimize_after_retina_2x_add', 10, 2 );
51 }
52
53 // соло оптимизация
54 add_filter( 'attachment_fields_to_edit', [ $this, 'attachmentEditorFields' ], 1000, 2 );
55 add_filter( 'manage_media_columns', [ $this, 'addMediaColumn' ] );
56 add_action( 'manage_media_custom_column', [ $this, 'manageMediaColumn' ], 10, 2 );
57 add_action( 'admin_enqueue_scripts', [ $this, 'enqueueMeadiaScripts' ], 10 );
58 add_action( 'delete_attachment', [ $this, 'deleteAttachmentHook' ], 10 );
59 add_action( 'wbcr/rio/optimize_template/optimized_percent', [ $this, 'optimizedPercent' ], 10, 2 );
60 add_action( 'wbcr/riop/queue_item_saved', [ $this, 'webpSuccess' ], 10, 1 );
61 }
62
63 /**
64 * @param int $attachment_id
65 * @param string $retina_file
66 */
67 public static function optimize_after_retina_2x_add( $attachment_id, $retina_file ) {
68 $metadata = get_post_meta( $attachment_id );
69 self::optimize_after_upload( $metadata, $attachment_id );
70 }
71
72 /**
73 * Оптимизация при загрузке в медиабиблиотеку
74 *
75 * @param array $metadata метаданные аттачмента
76 * @param int $attachment_id Номер аттачмента из медиабиблиотеки
77 *
78 * @return array $metadata Метаданные аттачмента
79 */
80 public static function optimize_after_upload( $metadata, $attachment_id ) {
81
82 $backup = WIO_Backup::get_instance();
83 $backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
84 $optimize_type = WRIO_Plugin::app()->getOption( 'image_optimization_type', 'schedule' );
85
86 if ( $backup_origin_images && ! $backup->isBackupWritable() ) {
87 return $metadata;
88 }
89
90 if ( wrio_is_license_activate() && $optimize_type == 'background' ) {
91 $processing = wrio_get_processing_class( 'media-library' );
92 if ( $processing && $processing->push_items( [ $attachment_id ] ) ) {
93 $processing->save()->dispatch();
94 }
95 } else {
96 WRIO_Cron::start_single( $attachment_id );
97 }
98
99 return $metadata;
100 }
101
102 /**
103 * Возвращает объект аттачмента
104 *
105 * @param int $attachment_id
106 * @param mixed $attachment_meta
107 *
108 * @return WIO_Attachment
109 */
110 public function getAttachment( $attachment_id, $attachment_meta = false ) {
111 if ( ! isset( $this->attachments[ $attachment_id ] ) ) {
112 $this->attachments[ $attachment_id ] = new WIO_Attachment( $attachment_id, $attachment_meta );
113 }
114
115 return $this->attachments[ $attachment_id ];
116 }
117
118 /**
119 * Оптимизирует аттачмент и со�
120 раняет статистику
121 *
122 * @param int $attachment_id
123 * @param string $level уровень оптимизации
124 *
125 * @return array
126 */
127 public function optimizeAttachment( $attachment_id, $level = '' ) {
128 $wio_attachment = $this->getAttachment( $attachment_id );
129 $optimization_data = $wio_attachment->getOptimizationData();
130
131 $allowed_mime = wrio_get_allowed_formats();
132 $allowed_mime = is_array( $allowed_mime ) ? $allowed_mime : [];
133 $mime_type = get_post_mime_type( $attachment_id );
134 if ( ! in_array( $mime_type, $allowed_mime, true ) ) {
135 WRIO_Plugin::app()->logger->warning( 'This format is disabled in the plugin settings: ' . $mime_type . ' (' . implode( ',', $allowed_mime ) . ')' );
136
137 return [];
138 }
139
140 if ( 'processing' == $optimization_data->get_result_status() ) {
141 return $this->deferredOptimizeAttachment( $attachment_id );
142 }
143
144 $image_statistics = WRIO_Image_Statistic::get_instance();
145 wp_suspend_cache_addition( true ); // останавливаем кеширование
146
147 if ( $wio_attachment->isOptimized() ) {
148 $this->restoreAttachment( $attachment_id );
149 $wio_attachment->reload();
150 }
151
152 $attachment_optimized_data = $wio_attachment->optimize( $level );
153 $original_size = $attachment_optimized_data['original_size'];
154 $optimized_size = $attachment_optimized_data['optimized_size'];
155 $image_statistics->addToField( 'optimized_size', $optimized_size );
156 $image_statistics->addToField( 'original_size', $original_size );
157 $image_statistics->save();
158 wp_suspend_cache_addition(); // возобновляем кеширование
159
160 // Reload attachment data to ensure cached object has fresh optimization data
161 $wio_attachment->reload();
162
163 return $attachment_optimized_data;
164 }
165
166 /**
167 * Отложенная оптимизация
168 *
169 * @param int $attachment_id
170 *
171 * @return bool|array
172 */
173 protected function deferredOptimizeAttachment( $attachment_id ) {
174 $wio_attachment = $this->getAttachment( $attachment_id );
175 $optimization_data = $wio_attachment->getOptimizationData();
176 $image_processor = WIO_OptimizationTools::getImageProcessor();
177
178 // если текущий сервер оптимизации не поддерживает отложенную оптимизацию, а в очереди есть аттачменты - ставим им ошибку
179 if ( ! $image_processor->isDeferred() ) {
180 $optimization_data->set_result_status( 'error' );
181
182 /**
183 * @var $extra_data RIO_Attachment_Extra_Data
184 */
185 $extra_data = $optimization_data->get_extra_data();
186 $extra_data->set_error( 'deferred' );
187 $extra_data->set_error_msg( 'server not support deferred optimization' );
188 $optimization_data->set_extra_data( $extra_data );
189 $optimization_data->save();
190
191 return false;
192 }
193
194 $optimized_data = $wio_attachment->deferredOptimization();
195 if ( $optimized_data ) {
196 $image_statistics = WRIO_Image_Statistic::get_instance();
197 $image_statistics->addToField( 'optimized_size', $optimized_data['optimized_size'] );
198 $image_statistics->addToField( 'original_size', $optimized_data['original_size'] );
199 $image_statistics->save();
200 }
201
202 return $optimized_data;
203 }
204
205 /**
206 * Восстанавливает аттачмент из резервной копии и со�
207 раняет статистику
208 *
209 * @param int $attachment_id
210 *
211 * @return bool|WP_Error
212 */
213 public function restoreAttachment( $attachment_id ) {
214 $image_statistics = WRIO_Image_Statistic::get_instance();
215 $wio_attachment = $this->getAttachment( $attachment_id );
216 $restored = $wio_attachment->restore();
217
218 if ( is_wp_error( $restored ) ) {
219 return $restored;
220 }
221
222 $optimization_data = $wio_attachment->getOptimizationData();
223 $optimized_size = $optimization_data->get_final_size();
224 $original_size = $optimization_data->get_original_size();
225 $webp_optimized_size = $optimization_data->get_extra_data()->get_webp_main_size();
226 $image_statistics->deductFromField( 'webp_optimized_size', $webp_optimized_size );
227 $image_statistics->deductFromField( 'optimized_size', $optimized_size );
228 $image_statistics->deductFromField( 'original_size', $original_size );
229 $image_statistics->save();
230 $optimization_data->delete();
231
232 /**
233 * Хук срабатывает после восстановления аттачмента
234 *
235 * @param RIO_Process_Queue $optimization_data
236 *
237 * @since 1.2.0
238 */
239 do_action( 'wbcr/rio/attachment_restored', $optimization_data );
240
241 return true;
242 }
243
244 /**
245 * Get ID's of unoptimized attachments
246 *
247 * @return array
248 */
249 public function getUnoptimizedImages() {
250 return WRIO_Image_Query::get_instance()->get_unoptimized_ids();
251 }
252
253 /**
254 * Get ID's of unconverted attachments for specified format
255 *
256 * @param string $format Format type: 'webp' or 'avif'. Default 'webp'.
257 *
258 * @return array
259 */
260 public function getUnconvertedImages( $format = 'webp' ) {
261 return WRIO_Image_Statistic::get_unconverted_images( $format );
262 }
263
264 /**
265 * Обработка не оптимизированны�
266 изображений
267 *
268 * @param int $max_process_per_request кол-во аттачментов за 1 запуск
269 *
270 * @return array|\WP_Error
271 */
272 public function processUnoptimizedImages( $max_process_per_request ) {
273 $backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
274
275 $backup = WIO_Backup::get_instance();
276
277 if ( $backup_origin_images && ! $backup->isBackupWritable() ) {
278 return new WP_Error( 'unwritable_backup_dir', __( 'No access for writing backups.', 'robin-image-optimizer' ) );
279 }
280
281 if ( ! $backup->isUploadWritable() ) {
282 return new WP_Error( 'unwritable_upload_dir', __( 'No access for writing backups.', 'robin-image-optimizer' ) );
283 }
284
285 $max_process_per_request = intval( $max_process_per_request );
286
287 // Get unoptimized attachment IDs using centralized query helper
288 $unoptimized_attachments_ids = WRIO_Image_Query::get_instance()->get_unoptimized_ids(
289 $max_process_per_request,
290 0,
291 true
292 );
293
294 // временно
295 $optimized_count = (int) RIO_Process_Queue::count_by_type_status( 'attachment', 'success' );
296 $attachments_count = ! empty( $unoptimized_attachments_ids ) ? sizeof( $unoptimized_attachments_ids ) : 0;
297 $total_unoptimized = WRIO_Image_Statistic::get_unoptimized_count();
298
299 $original_size = 0;
300 $optimized_size = 0;
301 $optimized_items = [];
302
303 // обработка
304 if ( ! empty( $attachments_count ) ) {
305
306 foreach ( $unoptimized_attachments_ids as $attachment_id ) {
307 $wio_attachment = $this->getAttachment( $attachment_id );
308
309 try {
310 if ( $wio_attachment->isOptimized() ) {
311 $this->restoreAttachment( $attachment_id );
312 $wio_attachment->reload();
313 }
314 $attachment_optimized_data = $wio_attachment->optimize();
315 $original_size = $original_size + $attachment_optimized_data['original_size'];
316 $optimized_size = $optimized_size + $attachment_optimized_data['optimized_size'];
317 $optimized_items[] = $attachment_id;
318 } catch ( Throwable $throwable ) {
319 $wio_attachment->mark_and_log_failure( $throwable, 'batch-processing' );
320 }
321 }
322 }
323
324 $image_statistics = WRIO_Image_Statistic::get_instance();
325
326 if ( $original_size > 0 || $optimized_size > 0 ) {
327 $image_statistics->addToField( 'optimized_size', $optimized_size );
328 $image_statistics->addToField( 'original_size', $original_size );
329 $image_statistics->save();
330 }
331
332 $remain = $total_unoptimized - $attachments_count;
333
334 // проверяем, есть ли аттачменты в очереди на отложенную оптимизацию
335 $optimized_data = $this->processDeferredOptimization();
336
337 if ( $optimized_data ) {
338 $optimized_count = $optimized_data['optimized_count'];
339 $remain = $total_unoptimized - $optimized_count;
340 }
341
342 if ( $remain <= 0 ) {
343 $remain = 0;
344 }
345
346 // Take the last optimized image ID. Used to log 100 optimized images.
347 $last_optimized_id = end( $optimized_items );
348
349 $response = [
350 'remain' => $remain,
351 'end' => false,
352 'statistic' => $image_statistics->load(),
353 'last_optimized' => $last_optimized_id ? $image_statistics->get_last_optimized_image( $last_optimized_id ) : [],
354 'optimized_count' => $optimized_count,
355 ];
356
357 return $response;
358 }
359
360 /**
361 * Convert unconverted images to WebP or AVIF format.
362 *
363 * @param int $max_process_per_request Number of attachments per request.
364 * @param string $format Target format: 'webp' or 'avif'. Default 'webp'.
365 *
366 * @return array|\WP_Error
367 *
368 * @since 1.5.3
369 */
370 public function webpUnoptimizedImages( $max_process_per_request, $format = 'webp' ) {
371 global $wpdb;
372
373 $db_table = RIO_Process_Queue::table_name();
374 $max_process_per_request = intval( $max_process_per_request );
375 $allowed_formats_sql = wrio_get_allowed_formats( true );
376
377 // Validate format
378 if ( ! in_array( $format, [ 'webp', 'avif' ], true ) ) {
379 $format = 'webp';
380 }
381
382 $optimize_order = WRIO_Plugin::app()->getOption( 'image_optimization_order', 'asc' );
383
384 // Convert only attachments that already have successful base optimization
385 // and do not yet have a queue record for the requested format.
386 $sql = $wpdb->prepare(
387 "SELECT DISTINCT posts.ID
388 FROM {$wpdb->posts} AS posts
389 WHERE posts.post_type = 'attachment'
390 AND posts.post_status = 'inherit'
391 AND posts.post_mime_type IN ( {$allowed_formats_sql} )
392 AND posts.ID IN(
393 SELECT object_id
394 FROM {$db_table} AS rio
395 WHERE rio.item_type = 'attachment'
396 AND rio.result_status = 'success'
397 GROUP BY object_id
398 )
399 AND posts.ID NOT IN(
400 SELECT object_id
401 FROM {$db_table} AS rio
402 WHERE rio.item_type = %s
403 GROUP BY object_id
404 )
405 ORDER BY posts.ID {$optimize_order}
406 LIMIT %d",
407 $format,
408 $max_process_per_request
409 );
410
411 // выборка не оптимизированны�
412 изображений
413 $unconverted_attachments_ids = $wpdb->get_col( $sql );
414
415 // временно
416 $attachments_count = ! empty( $unconverted_attachments_ids ) ? sizeof( $unconverted_attachments_ids ) : 0;
417 $total_unconverted = WRIO_Image_Statistic::get_unconverted_count( $format );
418 $converted_items = [];
419
420 // обработка
421 if ( ! empty( $attachments_count ) ) {
422
423 foreach ( $unconverted_attachments_ids as $attachment_id ) {
424 $wio_attachment = $this->getAttachment( $attachment_id );
425
426 try {
427 /**
428 * Fires after queue item was saved or updated successfully.
429 *
430 * @param RIO_Process_Queue $this
431 * @param bool $quota Deduct from the quota?
432 * @param string|null $format Format to convert to (webp, avif, or null for default)
433 */
434 do_action( 'wbcr/riop/queue_item_saved', $wio_attachment->getOptimizationData(), true, $format );
435
436 $converted_items[] = $attachment_id;
437 } catch ( Throwable $throwable ) {
438 $wio_attachment->mark_conversion_failure( $throwable, $format, sprintf( '%s-conversion-batch', $format ) );
439 }
440 }
441 }
442
443 $image_statistics = WRIO_Image_Statistic::get_instance();
444
445 $remain = $total_unconverted - $attachments_count;
446 if ( $remain <= 0 ) {
447 $remain = 0;
448 }
449
450 // Take the last converted image ID. Used to log 100 converted images.
451 $last_converted_id = end( $converted_items );
452
453 $response = [
454 'remain' => $remain,
455 'end' => false,
456 'statistic' => $image_statistics->load(),
457 'last_converted' => $image_statistics->get_last_converted_image( $last_converted_id, $format ),
458 'converted_count' => count( $converted_items ),
459 ];
460
461 return $response;
462 }
463
464 /**
465 * Конвертация в WebP не конвертированны�
466 изображений
467 *
468 * @param int $attachment_id
469 *
470 * @since 1.5.3
471 */
472 public function webpConvertAttachment( $attachment_id, $format = null ) {
473 $wio_attachment = $this->getAttachment( $attachment_id );
474 $optimization_data = $wio_attachment->getOptimizationData();
475
476 $image_statistics = WRIO_Image_Statistic::get_instance();
477
478 try {
479 /**
480 * Fires after queue item was saved or updated successfully.
481 *
482 * @param RIO_Process_Queue $this
483 * @param bool $quota Deduct from the quota?
484 * @param string|null $format Format to convert to (webp, avif, or null for default)
485 */
486 do_action( 'wbcr/riop/queue_item_saved', $optimization_data, true, $format );
487 } catch ( Throwable $throwable ) {
488 $wio_attachment->mark_conversion_failure( $throwable, $format, sprintf( '%s-conversion', $format ) );
489 }
490 }
491
492 /**
493 * Отложенная оптимизация
494 *
495 * @param int $attachment_id
496 *
497 * @return bool|array
498 */
499 protected function processDeferredOptimization( $attachment_id = 0 ) {
500 global $wpdb;
501 $db_table = RIO_Process_Queue::table_name();
502
503 if ( ! $attachment_id ) {
504 $attachment_id = $wpdb->get_var( "SELECT object_id FROM {$db_table} WHERE item_type = 'attachment' and result_status = 'processing' LIMIT 1;" );
505 }
506
507 if ( ! $attachment_id ) {
508 return false;
509 }
510
511 return $this->optimizeAttachment( $attachment_id );
512 }
513
514 /**
515 * Сбрасывает текущие ошибки оптимизации
516 * Позволяет изображениям, которые оптимизированы с ошибкой, заново пройти оптимизацию.
517 *
518 * @return void
519 */
520 public function resetCurrentErrors() {
521 // do_action( 'wbcr/rio/multisite_current_blog' );
522 global $wpdb;
523 $db_table = RIO_Process_Queue::table_name();
524 $wpdb->delete(
525 $db_table,
526 [
527 'item_type' => 'attachment',
528 'result_status' => 'error',
529 ],
530 [ '%s', '%s' ]
531 );
532 // do_action( 'wbcr/rio/multisite_restore_blog' );
533 }
534
535 /**
536 * Восстановление из резервной копии.
537 *
538 * @param int $max_process_per_request кол-во аттачментов за 1 запуск
539 *
540 * @return array
541 */
542 public function restoreAllFromBackup( $max_process_per_request ) {
543 if ( class_exists( 'WRIO_Cron' ) ) {
544 WRIO_Cron::stop();
545 }
546 WRIO_Plugin::app()->updatePopulateOption( 'cron_running', false ); // останавливаем крон
547
548 if ( WRIO_Plugin::app()->getPopulateOption( 'process_running', false ) ) {
549 $processing = wrio_get_processing_class( 'media-library' );
550 if ( $processing ) {
551 $processing->cancel_process();
552 }
553 }
554 WRIO_Plugin::app()->updatePopulateOption( 'process_running', false ); // останавливаем обработку
555
556 global $wpdb;
557
558 $db_table = RIO_Process_Queue::table_name();
559 $optimized_count = $wpdb->get_var( "SELECT COUNT(*) FROM {$db_table} WHERE item_type = 'attachment' AND result_status = 'success' LIMIT 1;" );
560 $optimized_attachments = $wpdb->get_results( "SELECT * FROM {$db_table} WHERE item_type = 'attachment' AND result_status = 'success' LIMIT " . intval( $max_process_per_request ) );
561
562 $attachments_count = 0;
563 if ( $optimized_attachments ) {
564 $attachments_count = count( $optimized_attachments );
565 }
566
567 $restored_count = 0;
568
569 // обработка
570 if ( $attachments_count ) {
571 foreach ( $optimized_attachments as $row ) {
572 $attachment_id = intval( $row->object_id );
573
574 $restored = $this->restoreAttachment( $attachment_id );
575 ++$restored_count;
576
577 if ( is_wp_error( $restored ) ) {
578 return [
579 'remain' => 0,
580 ];
581 }
582 }
583 }
584
585 $remane = $optimized_count - $restored_count;
586
587 if ( $remane === 0 ) {
588 // Should empty original/optimized size once all backups are empty
589 WRIO_Plugin::app()->updateOption( 'original_size', 0 );
590 WRIO_Plugin::app()->updateOption( 'optimized_size', 0 );
591 }
592
593 return [
594 'remain' => $remane,
595 ];
596 }
597
598 /**
599 * Кол-во оптимизированны�
600 изображений
601 *
602 * @return int
603 */
604 public function getOptimizedCount() {
605 return WRIO_Image_Query::get_instance()->count_optimized();
606 }
607
608 /**
609 * Add "Image Optimizer" column in the Media Uploader
610 *
611 * @param array $form_fields An array of attachment form fields.
612 * @param object $post The WP_Post attachment object.
613 *
614 * @return array
615 */
616 public function attachmentEditorFields( $form_fields, $post ) {
617 global $pagenow;
618
619 if ( 'post.php' === $pagenow ) {
620 return $form_fields;
621 }
622
623 $form_fields['wio'] = [
624 'label' => 'Image Optimizer',
625 'input' => 'html',
626 'html' => $this->getMediaColumnContent( $post->ID ),
627 'show_in_edit' => true,
628 'show_in_modal' => true,
629 ];
630
631 return $form_fields;
632 }
633
634 /**
635 * Add "wio" column in upload.php.
636 *
637 * @param array $columns An array of columns displayed in the Media list table.
638 *
639 * @return array
640 */
641 public function addMediaColumn( $columns ) {
642 $columns['wio_optimized_file'] = __( 'Robin Image Optimizer', 'robin-image-optimizer' );
643
644 return $columns;
645 }
646
647 /**
648 * Add content to the "wio" columns in upload.php.
649 *
650 * @param string $column_name Name of the custom column.
651 * @param int $attachment_id Attachment ID.
652 */
653 public function manageMediaColumn( $column_name, $attachment_id ) {
654 if ( 'wio_optimized_file' !== $column_name ) {
655 return;
656 }
657 echo $this->getMediaColumnContent( $attachment_id );
658 }
659
660 /**
661 * Возвращает шаблон для вывода блока кнопок на странице ручной оптимизации
662 *
663 * @param array $params @see calculateMediaLibraryParams()
664 * @param string $type Тип страницы
665 *
666 * @return string
667 */
668 public function getMediaColumnTemplate( $params, $type = 'media-library' ) {
669 require_once WRIO_PLUGIN_DIR . '/admin/includes/classes/class-rio-optimize-template.php';
670 $template = new WIO_OptimizePageTemplate( $type );
671
672 return $template->getMediaColumnTemplate( $params );
673 }
674
675 /**
676 * Выводит блок статистики для аттачмента в медиабиблиотеке
677 *
678 * @param int $attachment_id Номер аттачмента из медиабиблиотеки
679 *
680 * @return string
681 */
682 public function getMediaColumnContent( $attachment_id ) {
683 $params = $this->calculateMediaLibraryParams( $attachment_id );
684
685 return $this->getMediaColumnTemplate( $params );
686 }
687
688 /**
689 * Рассчитывает параметры для блока статистики в медиабиблиотеке
690 *
691 * @param int $attachment_id
692 *
693 * @return array @see WIO_OptimizePageTemplate::getMediaColumnTemplate()
694 */
695 public function calculateMediaLibraryParams( $attachment_id ) {
696 $wio_attachment = $this->getAttachment( $attachment_id );
697 $optimization_data = $wio_attachment->getOptimizationData();
698 $webp_data = $wio_attachment->getConversionData( 'webp' );
699 $avif_data = $wio_attachment->getConversionData( 'avif' );
700 $is_optimized = $optimization_data->is_optimized();
701 $is_skipped = $optimization_data->is_skipped();
702 $attach_meta = wp_get_attachment_metadata( $attachment_id );
703 $attach_dimensions = '0 x 0';
704 $error_msg = '';
705 // Check if attachment format is supported
706 $allowed_mime = wrio_get_allowed_formats();
707 $allowed_mime = is_array( $allowed_mime ) ? $allowed_mime : [];
708 $mime_type = get_post_mime_type( $attachment_id );
709 $is_supported_format = in_array( $mime_type, $allowed_mime, true );
710
711 $original_url = wp_get_attachment_url( $attachment_id );
712 $edit_url = get_edit_post_link( $attachment_id );
713 $original_name = basename( $original_url );
714
715 if ( isset( $attach_meta['width'] ) && isset( $attach_meta['height'] ) ) {
716 $attach_dimensions = $attach_meta['width'] . ' × ' . $attach_meta['height'];
717 }
718
719 clearstatcache();
720 $attachment_file = get_attached_file( $attachment_id );
721 $attachment_file_size = 0;
722
723 if ( $attachment_file && file_exists( $attachment_file ) ) {
724 $attachment_file_size = filesize( $attachment_file );
725 }
726
727 // Check for errors in extra data.
728 $extra_data = $optimization_data->get_extra_data();
729 if ( null !== $extra_data && method_exists( $extra_data, 'get_error_msg' ) ) {
730 $error = $extra_data->get_error_msg();
731
732 if ( ! empty( $error ) ) {
733 $error_msg = $error;
734 }
735 }
736
737 $extra_data = $webp_data->get_extra_data();
738 if ( null !== $extra_data && method_exists( $extra_data, 'get_error_msg' ) ) {
739 $error = $extra_data->get_error_msg();
740
741 if ( ! empty( $error ) ) {
742 $error_msg .= ' WebP error: ' . $error;
743 }
744 }
745
746 $extra_data = $avif_data->get_extra_data();
747 if ( null !== $extra_data && method_exists( $extra_data, 'get_error_msg' ) ) {
748 $error = $extra_data->get_error_msg();
749
750 if ( ! empty( $error ) ) {
751 $error_msg .= ' AVIF error: ' . $error;
752 }
753 }
754
755 if ( $is_optimized ) {
756 $optimized_size = $attachment_file_size;
757 $original_size = $optimization_data->get_original_size();
758
759 if ( empty( $optimized_size ) ) {
760 $original_size = $optimization_data->get_final_size();
761 }
762
763 /**
764 * @var $extra_data RIO_Attachment_Extra_Data
765 */
766 $extra_data = $optimization_data->get_extra_data();
767 $original_main_size = $original_size;
768 $thumbnails_optimized = 0;
769
770 if ( null !== $extra_data ) {
771 if ( method_exists( $extra_data, 'get_original_main_size' ) ) {
772 $original_main_size = $extra_data->get_original_main_size();
773 }
774 if ( method_exists( $extra_data, 'get_thumbnails_count' ) ) {
775 $thumbnails_optimized = $extra_data->get_thumbnails_count();
776 }
777 }
778
779 if ( empty( $original_main_size ) ) {
780 $original_main_size = $original_size;
781 }
782
783 $optimization_level = $optimization_data->get_processing_level();
784
785 if ( null !== $extra_data && method_exists( $extra_data, 'get_error_msg' ) ) {
786 $error_msg = $extra_data->get_error_msg();
787 }
788
789 $backuped = $optimization_data->get_is_backed_up();
790 $diff_percent = 0;
791 $diff_percent_all = 0;
792
793 if ( $attachment_file_size && $original_main_size ) {
794 $diff_percent = round( ( $original_main_size - $attachment_file_size ) * 100 / $original_main_size, 2 );
795 }
796
797 if ( $optimized_size && $original_size ) {
798 $diff_percent_all = round( ( $original_size - $optimized_size ) * 100 / $original_size, 2 );
799 }
800 } else {
801 $optimized_size = $optimized_size = $original_size = $original_main_size = false;
802 $thumbnails_optimized = $optimization_level = $backuped = $diff_percent = $diff_percent_all = false;
803 }
804
805 // Calculate WebP savings percentage
806 $webp_size = $webp_data->get_final_size();
807 $webp_percent = 0;
808 if ( $webp_size && $original_main_size ) {
809 $webp_percent = round( ( $original_main_size - $webp_size ) * 100 / $original_main_size, 2 );
810 }
811
812 // Calculate AVIF savings percentage
813 $avif_size = $avif_data->get_final_size();
814 $avif_percent = 0;
815 if ( $avif_size && $original_main_size ) {
816 $avif_percent = round( ( $original_main_size - $avif_size ) * 100 / $original_main_size, 2 );
817 }
818
819 if ( $webp_percent > 0 ) {
820 $diff_percent_all = max( $diff_percent_all, $webp_percent );
821 }
822
823 if ( $avif_percent > 0 ) {
824 $diff_percent_all = max( $diff_percent_all, $avif_percent );
825 }
826
827 $params = [
828 'attachment_id' => $attachment_id,
829 'is_supported_format' => $is_supported_format,
830 'is_optimized' => $is_optimized,
831 'attach_dimensions' => $attach_dimensions,
832 'attachment_file_size' => $attachment_file_size,
833 'optimized_size' => $optimized_size,
834 'original_size' => $original_size,
835 'original_main_size' => $original_main_size,
836 'thumbnails_optimized' => $thumbnails_optimized,
837 'optimization_level' => $optimization_level,
838 'error_msg' => $error_msg,
839 'backuped' => $backuped,
840 'diff_percent' => $diff_percent,
841 'diff_percent_all' => $diff_percent_all,
842 'is_skipped' => $is_skipped,
843 'webp_size' => $webp_size,
844 'avif_size' => $avif_size,
845 'webp_percent' => $webp_percent,
846 'avif_percent' => $avif_percent,
847 'webp_level' => $webp_data->get_processing_level(),
848 'avif_level' => $avif_data->get_processing_level(),
849 'original_name' => $original_name,
850 'original_url' => $original_url,
851 'edit_url' => $edit_url,
852 ];
853
854 return $params;
855 }
856
857 /**
858 * Добавляем стили и скрипты в медиабиблиотеку
859 */
860 public function enqueueMeadiaScripts( $hook ) {
861 if ( $hook != 'upload.php' ) {
862 return;
863 }
864 wp_enqueue_style( 'wio-install-addons', WRIO_PLUGIN_URL . '/admin/assets/css/media.css', [], WRIO_Plugin::app()->getPluginVersion() );
865 wp_enqueue_script( 'wio-install-addons', WRIO_PLUGIN_URL . '/admin/assets/js/single-optimization.js', [ 'jquery' ], WRIO_Plugin::app()->getPluginVersion() );
866 }
867
868 /**
869 * Выполняется при удалении аттачмента из медиабиблиотеки
870 */
871 public function deleteAttachmentHook( $attachment_id ) {
872 $wio_attachment = new WIO_Attachment( $attachment_id );
873 if ( $wio_attachment->isOptimized() ) {
874 $this->restoreAttachment( $attachment_id );
875 }
876 }
877
878 /**
879 * Возвращает процент оптимизации
880 * Фильтр wbcr/rio/optimize_template/optimized_percent
881 *
882 * @param int $percent процент оптимизации
883 * @param string $type тип страницы
884 *
885 * @return int процент оптимизации
886 */
887 public function optimizedPercent( $percent, $type ) {
888 if ( 'media-library' == $type ) {
889 $image_statistics = WRIO_Image_Statistic::get_instance();
890
891 return $image_statistics->getOptimizedPercent();
892 }
893
894 return $percent;
895 }
896
897 /**
898 * Со�
899 раняет WebP размер
900 *
901 * @param RIO_Process_Queue $queue_model
902 *
903 * @return bool
904 */
905 public function webpSuccess( $queue_model ) {
906 if ( ! class_exists( 'WRIO\WEBP\Listener' ) ) {
907 return false; // если не установлена премиум версия, то WebP не активен
908 }
909
910 if ( $queue_model->get_item_type() !== WRIO\WEBP\Listener::DEFAULT_TYPE ) {
911 return false;
912 }
913
914 if ( $queue_model->get_result_status() !== RIO_Process_Queue::STATUS_SUCCESS ) {
915 return false;
916 }
917
918 /**
919 * @var $extra_data RIO_Attachment_Extra_Data
920 */
921 $extra_data = $queue_model->get_extra_data();
922 $item_type = $extra_data->get_convert_from();
923 if ( 'attachment' != $item_type ) {
924 return false;
925 }
926
927 $object_id = $queue_model->get_object_id();
928 if ( ! $object_id ) {
929 return false;
930 }
931 $src = wp_get_attachment_image_src( $object_id, 'full' );
932
933 if ( false !== $src ) {
934 $src = $src[0];
935 }
936
937 $url_hash = hash( 'sha256', $src );
938 if ( $queue_model->get_item_hash() == $url_hash ) {
939 $optimization_data = new RIO_Process_Queue(
940 [
941 'object_id' => $object_id,
942 'item_type' => 'attachment',
943 ]
944 );
945 $optimization_data->load();
946 $extra_data = $optimization_data->get_extra_data();
947 if ( $extra_data ) {
948 $extra_data->set_webp_main_size( $queue_model->get_final_size() );
949 }
950 $optimization_data->set_extra_data( $extra_data );
951 add_filter( 'wbcr/riop/queue_item_save_execute_hook', '__return_false' );
952 $optimization_data->save();
953 remove_filter( 'wbcr/riop/queue_item_save_execute_hook', '__return_false' );
954 }
955
956 return true;
957 }
958 }
959